题目大意:
已知有n个砝码,我们从中可以任意去掉m个,问我们最多可以得到多少种不同的重量。
解题思路:
首先,我们把解题分为两个思路:首先,我们看任意去掉m个。
这个我们可以用DFS模拟,关键在于停止条件。我们知道我们每一步都可以选择添加或者不添加某个砝码,然后重复n次。
这样我们就有最简单的递归停止条件,递归到第n次我们开始可以看看删除的数目是多少,若是m,我们就进入下一步。但显然,我们发现,其实若递归过程中删除的数目大于m,我们其实是可以停止递归的。
得到哪些砝码需要后,我们进入下一步计数,统计有多少种不同重量。
我们设立状态memo[i][j],表示若我们遍历到第j个重量为i是否可行,可行memo[i][j]=1,若不可行我们把memo[i][j]=0.
这里转移首先:若
memo[i][j-1]=1,那么memo[i][j]=1
若memo[i][j-1]=0,是不是代表memo[i][j]=0呢?
不一定,我们假设砝码数列为a1 a2 a3 ...
假设
若memo[i-ai][j-1]=1则memo[i][j]=1
注意i的枚举范围,0<=i<=sum+a[i]
其中sum为前面i-1个的数列求和。
废话:
提高组的题目是越来越难了,现在题目往往是将几个算法结合起来考,所以我们不应该只用单一算法去解了。
另外这里的dp也很有意思,我们发现状态这里的设置有点像01DP,【第几个】【重量】,只不过这里的取值只有0或者1两种可能。
另外DFS回溯的时候,一般是需要有一个状态去记录我们走去了第几层。
#include <bits/stdc++.h>
using namespace std;
const int MAXN =25;
int tar[MAXN];
int ans;
int n,m;
int ls;
vector<int> arrmv;
int f[MAXN*100][MAXN];
void dp(){
int tot=0;
int sum=0;
int weight[MAXN*100];
memset(weight,0,sizeof(weight));
memset(f,0,sizeof(f));
f[0][0]=1;
int lstep=0;
for(int j=1;j<=n;j++){
if(!tar[j-1])continue;
for(int i=0;i<=sum+arrmv[j-1];i++){
if(i>=arrmv[j-1]){
if(f[i][lstep])f[i][j]=1;
else {
f[i][j]=f[i-arrmv[j-1]][lstep];
}
}else f[i][j]=f[i][lstep];
if(f[i][j]==0)continue;
if(i!=0 && weight[i]==0){
//cerr<<"weight "<<i<<" ++"<<endl;
weight[i]=1;
tot+=f[i][j];
}
}
lstep=j;
sum+=arrmv[j-1];
}
ans=max(tot,ans);
}
void dfs(int cur,int del){
//cerr<<cur<<" " <<del<<endl;
if(del>m )return;
if(cur==n && del==m){
// for(int i=0;i<n;i++)
// cerr<<tar[i]<<" ";
// cerr<<endl;
dp();
}
if(cur==n)return;
tar[cur]=1;
dfs(cur+1,del);
tar[cur]=0;
ls=cur;
dfs(cur+1,del+1);
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
int t;cin>>t;
arrmv.push_back(t);
}
ans=-1;
//memset(tar,-1,sizeof(tar));
dfs(0,0);
cout<<ans<<endl;
return 0;
}