新年趣事之打牌
★ 输入文件:bagb.in
输出文件:
bagb.out
简单对比
时间限制:1 s 内存限制:128 MB
【题目描述】
过年的时候,大人们最喜欢的活动,就是打牌了。xiaomengxian不会打牌,只好坐在一边看着。
这天,正当一群人打牌打得起劲的时候,突然有人喊道:“这副牌少了几张!”众人一数,果然是少了。于是这副牌的主人得意地说:“这是一幅特制的牌,我知道整副牌每一张的重量。只要我们称一下剩下的牌的总重量,就能知道少了哪些牌了。”大家都觉得这个办法不错,于是称出剩下的牌的总重量,开始计算少了哪些牌。由于数据量比较大,过了不久,大家都算得头晕了。
这时,xiaomengxian大声说:“你们看我的吧!”于是他拿出笔记本电脑,编出了一个程序,很快就把缺少的牌找了出来。
如果是你遇到了这样的情况呢?你能办到同样的事情吗?
【输入格式】
第一行一个整数TotalW,表示剩下的牌的总重量。
第二行一个整数N(1<=N<=100)<n<=100),表示这副牌有多少张。< span="">
接下来N行,每行一个整数Wi(1<=Wi<=1000),表示每一张牌的重量。
【输出格式】
如果无解,则输出“0”;如果有多解,则输出“-1”;否则,按照升序输出丢失的牌的编号,相邻两个数之间用一个空格隔开。
【样例输入】
270 4 100 110 170 200
【样例输出】
2 4
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int m,n,L=0,Max=0;
int a[101]= {0},path[100001]= {0}; //path记录编号,dp含义是到达容量V的时候有多少种方法
int dp[100001]= {0}; //0-1背包问题
int ans[101]= {0}; //重量能够达到m时,用ans数组牌的编号
void read()
{
freopen("bagb.in","r",stdin);
freopen("bagb.out","w",stdout);
int i;
scanf("%d%d",&m,&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
}
void DP()
{
dp[0]=1;
int i,j,k;
for(i=1; i<=n; i++)
{
for(j=Max; j>=0; j--)
if(dp[j])
{
dp[j+a[i]]++;
if(dp[j+a[i]]==1) //*******************************************//
path[j+a[i]]=i; //在这里要着重考虑,当背包的大小第一次达到j+a[i]时,我们在j+a[i]这个位置记录i,代表着当容量首次达到j+a[i]时,取的是a[i],在最后我们从总容量m逆向向前查找a[i],找到的a[i]就是手中牌的编号
if(j+a[i]>Max) //这个Max是为了简化第二重循环的,在j>Max时,dp[j]都是0
Max=j+a[i];
}
}
}
void DFS(int M) //用了搜索求的ans,根据总容量求ans,当首次到达M这个容量时,我们可以找到使j+a[i]==M时的那个i,记录在ans中,然后,用M-a[i],新的结果再作为M,再用DFS,直至M=0.
{
if(M==0)
return ;
DFS(M-a[path[M]]);
ans[L++]=path[M];
}
//why:M一定会等于0吗?
//answer:是的,因为在下面我只是在满足只存在一种组合使得m(就是题目给的总重量)存在时,才调用DFS。
void print()
{
int i,j,k;
if(dp[m]==0) //到达m时方法数为0
{
printf("0");
}
else if(dp[m]>=2) //到达m不止一种方法
{
printf("-1");
}
else //到达m时仅有一种方法,调用DFS
{
DFS(m);
for(i=0; i<L; i++) //把ans中的编号都去掉,剩下的就是要输出的
a[ans[i]]=0;
for(i=1; i<=n; i++)
if(a[i])
printf("%d ",i);
}
}
int main()
{
read(); //全部才用函数调用,这样做,需要熟练使用全局变量。
DP();
print();
return 0;
}
/*
3072
10
160
531
64
498
188
933
811
5
879
83
3 6 10
*/