题目链接
解题思路:
搜索/枚举+dp
dp过程参考弱化版的P2347 砝码称重。
妙啊,真的是妙啊…
感觉这题搜索和dp结合的恰到好处。利用dfs先枚举出所有可能的情况,当数组
a
[
i
]
a[i]
a[i]中被标记的数值达到
m
m
m后,表示已经舍弃了
m
m
m个值,在剩下的
n
−
m
n-m
n−m个值里面dp就行了。
在dp过程,由于题目数据范围可知,所有砝码的和一定不超过2000,所以用一个数组
f
[
i
]
f[i]
f[i]表示
n
−
m
n-m
n−m个砝码组合得到的和可以是
i
i
i,然后统计
f
[
i
]
f[i]
f[i]里面不为0的个数就可以了。
最后注意在更新
f
[
i
]
f[i]
f[i]的过程中,逆序更新,防止之前更新的值影响已经更新过的值。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>//memset
using namespace std;
int n,m;
int a[30];
int vis[30],now=1;
int f[2010];//f[0]=1;
int ans=0;
void dp(){
memset(f,0,sizeof(f));
f[0]=1;
for(int i=1;i<=n;i++){
if(!vis[i]){
for(int j=2000;j>=a[i];j--){
if(f[j-a[i]]&&!f[j])
f[j]=1;
}
}
}
int cnt=0;
for(int i=1;i<=2000;i++){
if(f[i]) cnt++;
}
ans=max(ans,cnt);
}
void dfs(int k){
if(k==m) {
dp();
return;
}
for(int i=now;i<=n;i++){
if(!vis[i]){
vis[i]=1;
now=i+1;
dfs(k+1);//计数,枚举
vis[i]=0;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
dfs(0);
printf("%d\n",ans);
return 0;
}