P1441 砝码称重

dfs+小剪枝+背包方案数的一道题

这道题有一个前置技能:你要会求那\(n-m\)个砝码能表示多少的重量。

因为在这道题里面每个砝码只能取一次,所以相当于一个少了一个属性的01背包方案数。套上相应内容即可求解。

这应该也能有所帮助:https://www.cnblogs.com/Garen-Wang/p/9800949.html

然后就能看这道题了。

前面的东西都不用过脑子就知道是dfs。

所以可以像普通的dfs那样去掉\(m\)个然后套上上面的01背包方案数去求解。

但是dfs过程就有点慢了,T了好几个点。

所以附上一个剪枝:

去掉\(m\)个砝码这个操作是无序的,而如果你认为是有序的话你就会多枚举出了好几次相同的组合。

解决方法:记录上次取的砝码的下标是哪个,然后下一层dfs就从下一位开始。

这个东西确实挺重要的,积累下来吧。

所以就能过了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 21;

int a[maxn];
bool vis[maxn];
int weight[maxn];
int dp[2005];
int n, m;
int ans = -0x3f3f3f3f;
void dfs(int t, int pos)
{
    if(t == m + 1)
    {
        memset(dp, 0, sizeof dp);
        dp[0] = 1;
        int len = 0;
        for(int i = 1; i <= n; i++) if(!vis[i]) weight[++len] = a[i];
        for(int i = 1; i <= len; i++)
        {
            for(int j = 2000; j >= weight[i]; j--)
            {
                dp[j] += dp[j - weight[i]];
            }
        }
        int res = 0;
        for(int i = 1; i <= 2000; i++) if(dp[i]) res++;
        ans = std::max(ans, res);
    }
    else
    {
        for(int i = pos; i <= n; i++)
        {
            if(!vis[i])
            {
                vis[i] = true;
                dfs(t + 1, i + 1);
                vis[i] = false;
            }
        }
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    dfs(1, 1);
    printf("%d\n", ans);
    return 0;
}
posted @ 2018-10-18 21:35  Garen-Wang  阅读(129)  评论(0编辑  收藏  举报