P1441 砝码称重 (dfs+dp或者bitset)

P1441 砝码称重

原题链接
题目描述
现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0)。

请注意,砝码只能放在其中一边。

输入格式
输入文件weight.in的第1行为有两个整数n和m,用空格分隔

第2行有n个正整数a1,a2,a3,……,an,表示每个砝码的重量。

输出格式
输出文件weight.out仅包括1个整数,为最多能称量出的重量数量。

输入输出样例
输入 #1
3 1
1 2 2
输出 #1
3
说明/提示【样例说明】

在去掉一个重量为2的砝码后,能称量出1,2,3共3种重量。

【数据规模】

对于20%的数据,m=0;

对于50%的数据,m≤1;

对于50%的数据,n≤10;

对于100%的数据,n≤20,m≤4,m<n,ai≤100。

题意:给出n个砝码及其质量,去掉m个砝码后,问最多能称量出多少不同的重量(不包括0)。
思路
1.自然首先想到的是dfs,求出各种砝码的组合,再借助01背包求得不同种类质量的数量。
(状态转移方程f[i]=f[weight+a[i]].)(0=<weight<=当前最大质量和)且是从大到小列举,顺序不可反.
2.看了大佬的题解后,有一种更加巧妙的方法即是利用二进制的特性罗列出所有的可能,然后使用bitset和“|”运算符解决累加得到不同可能性冲突的问题,而且读出结果也十分的方便(使用.count得到“1”的数量即是该种可能的结果).
屁话少说贴代码了:

#include <bits/stdc++.h>
using namespace std;
int n,m,weigt[50];
int count1(int x)//得到该数字的二进制中“1”的个数.
{
    int sum=0;
    for(int i=0; i<n; i++)
    {
        if(x&(1<<i))
            sum++;
    }
    return sum;
}
int main()
{
    int ans=-1;
    scanf("%d %d",&n,&m);
    for(int i=0; i<n; i++)//使用二进制解题下标最好从0开始.
        scanf("%d",&weigt[i]);
    for(int i=0; i<(1<<n); i++)//时间复杂度o(2^n).
    {
        if(count1(i)==n-m)//“1”的个数为n-m则统计组合数.
        {
            bitset<2010>st;
            st[0]=1;//置st[0]=1至关重要,不可省去,与后面位移和与运算有很大关系.
            for(int j=0; j<n; j++)
            {
                if(i&(1<<j))
                    st=st|(st<<weigt[j]);//点睛之笔,只可意会QAQ.
            }
            int k=st.count();
            ans=max(ans,k);
        }
    }
    cout<<ans-1<<endl;//减去“0”.
    return 0;
}

总结:emm…通过这个学到了利用二进制解决组合数问题,巧妙的规避了重复的难点,不过dp的01背包也有同样的效果,只是代码量稍微大了那么一丢丢.
其余题解链接
少做水题有利于人变得更水,(__) 嘻嘻…… bye!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值