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!