大致思路:
首先看一下这道题:https://blog.csdn.net/m0_38033475/article/details/80380467
你对比一下会发现,都是求“方案数”的,其实都是用“01背包”来做的:对本题来说,f[j]的值表示重量为j时的方案数(每个方案的重量和要不一样)。但是你会发现其实两题是不一样的,是在于题意不同,本题求的不是单纯的“方案数”,而是不同的重量和有多少种,然而,显然,有可能多个方案拥有一样的重量和,所以不能单纯以求得的方案数拿来当重量和。
所以就要思考,怎样的方案是算重复的?它所得的重量和要相等,而重量和相等则意味着——砝码的重量组合其实是一样的!而我已经保证用dfs去依次加砝码了,为什么会出现重量组合相同的情况?其实根本原因就是因为重量其实是无序排列的,比如“1 2 3”和“1 3 2”,或者出现“1 1 1”和“1 1 1”(所以你即使如果先把数据给sort了,也需要在循环中去比较不能和上一个放入的相同),就算有相同元素都不行(如“1 1 3”和“1 2 2”的可能)。
anyway,记住一句总结:用“单调性”解决“组合重复性”。
使数据单调在这里有两种方法:
- 在读入后加入一个Sort。(还是需要遍历每一个砝码并且做处理不能和上一个相等)
- 用桶存储数据。(只需要遍历数据的范围(本题砝码重量最大才100,因此完全可以大大优化))
其实都是排序啦。
考虑用桶存储数据
优点:在读入之后没有额外的复杂度
缺点:可能需要更多的时间来遍历到所有数据
再看看桶的大小, a_{i}ai <=100,缺点完全可以忽略,用桶存储可行!
这是我第一次接触桶排序,小小地用自己的话总结一下:
首先是a数组,a[v]表示v这个数据值有多少个。
其实就是输入数据的时候,比如数据为v,则使a[v]++。把数据中的最小值和最大值记录一下,那么我在dfs遍历的时候就可以“单调”选取,代码如下:
for(int i=last;i<=max_nums;i++)//这里很重要。保证了pack数组的单调性,进而使得其不重复 //last是当前dfs遍历到的数据大小,max_nums是最大数据
{
if (a[i]>0)
{
a[i]--;
pack[x]=i;
dfs(x+1,i);
a[i]++;
}
#include<bits/stdc++.h>
using namespace std;
int a[107],pack[21],ans=0,n,m,max_nums=0,min_nums=999,num;
bool f[3000];
void dp()
{
memset(f,0,sizeof(f));f[0]=1;
int sum=0,tot=0;
for(int i=1;i<=num;i++) sum+=pack[i];
for(int i=1;i<=num;i++)
{
for(int j=sum;j>=pack[i];j--)
f[j]=f[j]+f[j-pack[i]];
}
for(int i=1;i<=sum;i++)
{
if (f[i]) tot++;
}
ans=max(ans,tot);
}
void dfs(int x,int last)
{
if (x>num)
{
dp();
return;
}
for(int i=last;i<=max_nums;i++)//这里很重要。保证了pack数组的单调性,进而使得其不重复
{
if (a[i]>0)
{
a[i]--;
pack[x]=i;
dfs(x+1,i);
a[i]++;
}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
int v;
scanf("%d",&v);
max_nums=max(max_nums,v);
min_nums=min(min_nums,v);
a[v]++;
}
num=n-m;//留下n-m个砝码
dfs(1,min_nums);//从最小值进行搜索
printf("%d",ans);
return 0;
}