砝码称重
题目描述
现有 $n$ 个砝码,重量分别为 $a_i$,在去掉 $m$ 个砝码后,问最多能称量出多少不同的重量(不包括 $0$)。
请注意,砝码只能放在其中一边。
输入格式
第 $1$ 行为有两个整数 $n$ 和 $m$,用空格分隔。
第 $2$ 行有 $n$ 个正整数 $a_1, a_2, a_3,\ldots , a_n$,表示每个砝码的重量。
输出格式
仅包括 $1$ 个整数,为最多能称量出的重量数量。
样例 #1
样例输入 #1
3 1
1 2 2
样例输出 #1
3
提示
【样例说明】
在去掉一个重量为 $2$ 的砝码后,能称量出 $1, 2, 3$ 共 $3$ 种重量。
【数据规模】
对于 $20%$ 的数据,$m=0$。
对于 $50%$ 的数据,$m\leq 1$。
对于 $50%$ 的数据,$n\leq 10$。
对于 $100%$ 的数据,$n\leq 20$, $m\leq 4$,$m < n$,$a_i\leq 100$。
DP真的太难了!!!
搜索过程真的没什么感觉,就普通的组合在加记录,因为数据比较水所以没有太多优化
伪代码如下:
inline void fao(int c, int d) {
if (c == m) {
k++;
for (int i = 1, j = 0; i <= n; i++) {
if (vis[i] == 0) {
a[k][++j] = b[i];
}
}
return;
}
for (int i = d + 1; i <= n; i++) {
if (vis[i] == 1)
continue;
vis[i] = 1;
fao(c + 1, i);
vis[i] = 0;
}
return;
}
当然如果想要优化可以加一个记录和判断
因为这种做法会有一些重复
比如:
(1, 2, 2) 这组数据会产生 (1, 2) (1, 2) (2, 2) 这三种组合,可以发现有两组是相同的
所以,可以用一个 vis[i][j]
来维护(其中i记录位数,$j$ 记录当前位数的数),如果有完全相同的两组,就删掉一组
DP太难了!!!
先看伪代码吧
inline int check(int c) {
int cnt = 0, sum = 0;
bool dp[2060] = {0};
for (int i = 1; i <= n - m; i++) {
sum += a[c][i];
}
dp[0] = 1;
for (int j = 1; j <= n - m; j++) {
for (int i = sum; i >= 0; i--) {
if (i >= a[c][j]) {
dp[i] = dp[i - a[c][j]] || dp[i];
}
}
}
for (int i = 1; i <= sum; i++) {
if (dp[i])
cnt++;
}
return cnt;
}
dp[i][j]
记录用第i个砝码,$j$ 记录能称出的质量
再加上滚动数组优化……
因此状态转移方程就为 dp[i] = dp[i - a[j]] || dp[i]
(这个或运算很重要!!!)因为如果原来为真,在不加或的时候可能会出现判断为假!!!
Comments NOTHING