题目描述
现有
n
个砝码,重量分别为
输入输出格式
输入格式:
输入文件weight.in的第1行为有两个整数
n
和
第2行有
n
个正整数
输出格式:
输出文件weight.out仅包括1个整数,为最多能称量出的重量。
输入输出样例
输入样例#1:
3 1
1 2 2
输出样例#1:
3
说明
【样例说明】
在去掉一个重量为
2
的砝码后,能称量出
【数据规模】
对于20%的数据,
m=0
;
对于50%的数据,
m≤1
;
对于50%的数据,
n≤10
;
对于100%的数据,
n≤20,m≤4,m<n,ai≤100
。
解题报告
这道题我们要解决两个问题,一个是处理出去掉m个砝码有多少种方案,第二个是要在一个确定的方案上找出这个方案最多能称出多少重量不同的物品,也就是砝码组合起来有多少种重量不同的情况。
对于第一个问题,我们采用搜索的方法解决,我们的dfs有两个参数,分别是当前搜索到的位置(u)和已经去掉的砝码个数(step)。
有两个显然的终止条件,一个是
step>m
,另一个是
u=n+1
(u的初始值为1)。
一个搜索到的点u,他的下一个状态(u+1)有去掉它和不去掉它两种选项,由此我们就要分别写两个dfs分开搜索。
对于第二个问题,我们采用背包问题的解法来解决。也就是开一个
dp[j]
,表示砝码重量总计为i时的方案是否可行。显然,
dp[j]
从
dp[j−a[i]]
转移过来。
这样一来,剩下的就很明朗了:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20,M=2000;
int n,m,a[N+5],vmax,ans;
bool dp[M+5],flag[N+5];
void dfs(int u,int step)
{
if(step>m)return ;
if(u==n+1)
{
if(step==m)
{
int tot=0;
for(int i=1;i<=vmax;i++)dp[i]=false;
for(int i=1;i<=n;i++)
{
if(flag[i])continue;
for(int j=vmax;j>=a[i];j--)
if(dp[j-a[i]])dp[j]=true;
}
for(int i=1;i<=vmax;i++)if(dp[i])tot++;
ans=max(ans,tot);
}
return ;
}
dfs(u+1,step);
flag[u]=1;
dfs(u+1,step+1);
flag[u]=0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),vmax+=a[i];
dp[0]=true;
dfs(1,0);
printf("%d",ans);
return 0;
}