题解 P1441 【砝码称重】

Xial 发布于 2019-09-16 0 次阅读


砝码称重

题目描述

现有 $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](这个或运算很重要!!!)因为如果原来为真,在不加或的时候可能会出现判断为假!!!

综上……,综上个鬼

最后更新于 2022-09-16