题目
Description
过年的时候,大人们最喜欢的活动,就是打牌了。xiaomengxian不会打牌,只好坐在一边看着。
这天,正当一群人打牌打得起劲的时候,突然有人喊道:“这副牌少了几张!”众人一数,果然是少了。于是这副牌的主人得意地说:“这是一幅特制的牌,我知道整副牌每一张的重量。只要我们称一下剩下的牌的总重量,就能知道少了哪些牌了。”大家都觉得这个办法不错,于是称出剩下的牌的总重量,开始计算少了哪些牌。由于数据量比较大,过了不久,大家都算得头晕了。
这时,xiaomengxian大声说:“你们看我的吧!”于是他拿出笔记本电脑,编出了一个程序,很快就把缺少的牌找了出来。
如果是你遇到了这样的情况呢?你能办到同样的事情吗?
格式Input
第一行一个整数TotalW,表示剩下的牌的总重量。
第二行一个整数N(1<N<=100)
,表示这副牌有多少张。
接下来N行,每行一个整数Wi(1<=Wi<=1000)
,表示每一张牌的重量。Output
如果无解,则输出“0”;如果有多解,则输出“-1”;否则,按照升序输出丢失的牌的编号,相邻两个数之间用一个空格隔开。
样例1Sample Input
270
4
100
110
170
200Sample Output
2 4
HINT
各个测试点1s
分析
- 其实是道简单的
01背包
问题,只是需要你把具体的方案写出来罢了。 - 开始我有一个暴力的想法,就是把每个状态的方案都记录下来(要是多解的话就不理它了),想了想用了个
bitset
记这种状态每张牌是否要选,竟然过了,不过空间是极大的。 - 后来想了想,用了个类似链表的思想,把到达这个状态要选的最后一张牌记下,那么最后要是确定解唯一,就可以倒过来推出所有要选的牌了,写出来,空间和时间果然都优化了不少。
- 有一点注意,它是最多
100
张牌,每张牌最重1000
,所以有100000
种状态。
代码
第一种方法的代码
#include <cstdio>
#include <bitset>
using namespace std;
int W,N,w,s,f[100010];
bitset <105> g[100010];
int main(){
f[0]=1;
scanf("%d%d",&W,&N);
for (int i=1; i<=N; i++){
scanf("%d",&w);
s+=w;
for (int j=s; j>=w; j--){
if (f[j-w]==1){
if (f[j]==0) f[j]=1,g[j]=g[j-w],g[j][i]=1;
else f[j]++;
}
if (f[j-w]>1) f[j]=3;
}
}
if(f[W]>1) printf("-1");
else if (f[W]==0)printf("0");
if (f[W]==1) for (int i=1; i<=N; i++) if (!g[W][i]) printf("%d ",i);
return 0;
}
第二种方法的代码
#include <cstdio>
int W,N,s,w[105],f[100005],e[100005];
int main(){
f[0]=1;
scanf("%d%d",&W,&N);
for (int i=1; i<=N; i++) scanf("%d",&w[i]);
for (int i=(s=w[N],N); i; i--,s+=w[i]){
for (int j=s; j>=w[i]; j--) if (f[j-w[i]]){
if (f[j-w[i]]==1) if (f[j]==0) f[j]=1,e[j]=i; else f[j]=2;
if (f[j-w[i]]>1) f[j]=2;
}
}
s-=W;
if (f[s]==0) printf("0");
if (f[s]==1) for (int i=s; i; i-=w[e[i]]) printf("%d ",e[i]);
if (f[s]>1) printf("-1");
return 0;
}
提示
给出两种方法的评测结果:
# | 总耗时 | 峰值内存 |
---|---|---|
1 | 59ms | 2.32MiB |
2 | 22ms | 928.0KiB |