Gy最近学习了01背包问题,无聊的他又想到了一个新的问题,给定n个物品的价值,和01背包一样,每个物品只能选1次或0次,求最小不能被得到的价值。
Gy最近学习了01背包问题,无聊的他又想到了一个新的问题,给定n个物品的价值,和01背包一样,每个物品只能选1次或0次,求最小不能被得到的价值。
第一行一个正整数T(T <= 100),表示有T组数据。
每组数据输入格式如下:
第一行为一个正整数N(N<=100),表示物品个数。
第二行N个正整数,表示每个物品的价值vi(1<=vi<=1000000)
共输出T行,即每组数据相应答案。
2
3
2 4 8
4
1 2 4 8
1
16
本题看似dp问题,但观察物品价值的范围即可发现普通的背包解法是无法在规定时间内解决的,所以需要换一种思路来考虑
看第二组样例数据,明显是在提示我们去想1 2 4 8 16 32。。。这个数列a[1]=1, a[i]=a[i-1]*2,这个数列的特点是其前i项,可以通过加法组合得到所有小于第i+1项的数。
例如对于前3项1 2 4这三个数,
1=1
2=2
3=1+2
4=4
5=1+4
6=2+4
7=1+2+4
而a[4]=8,1~7都可得到。
方便记忆,将1 2 4 8 16。。。这个数列记为数列cp,先通过以下程序打出cp数列,因为已经给定物品价值范围不会大于1000000,所以只需算到刚大于1000000那一项即可
#include<iostream> using namespace std; int main() { int i=1; freopen("out.txt","w",stdout); cout<<i<<‘,‘; while(true) { i=(i<<1); cout<<i<<‘,‘; if(i>1000000) break; } return 0; }
1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,
求解步骤如下:
以a为1 2 3 4为例
1、尽量从a序列中依序取出cp数列中的元素;
(即取出了1 2 4,1 2 4可表示出1~7,记ans=8
2、从a剩下的数中取最小值m
1)若m>ans,最终结果即为ans,完成!
2)若m<=ans,ans+=m,从a中移除m,goto 2
(即对于a中经过第1步,剩下的数字为3,3<8,那么:8=3+5=3+(1+4), 9=3+6=3+(2+4), 10=3+7=3+(1+2+4),即ans=3+8=11
若对于a为1 2 4 9,因为9>8,8就无法取到了,所以得到最终答案为8.
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<vector> #include<list> using namespace std; int main() { vector<int> ain; list<int> a; int num; int cp[50]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576}; while(cin>>num) { while(num--!=0) { int n,pb; cin>>n; ain.clear(); a.clear(); for(int i=1;i<=n;++i) { scanf("%d",&pb); ain.push_back(pb); } sort(ain.begin(),ain.end()); for(int i=0;i!=ain.size();++i) { a.push_back(ain[i]); } int ans=1,j=0; for(list<int>::iterator i=a.begin();i!=a.end()&&j<20; ) { if(*i==cp[j]) { ans=cp[j+1]; i=a.erase(i); ++j; continue; } ++i; } for(list<int>::iterator i=a.begin();i!=a.end();++i) { if(*i>ans) { break; } else { ans+=(*i); } } cout<<ans<<endl; } } return 0; }
poj 2492 A Bug's Life(带权并查集),布布扣,bubuko.com
原文:http://blog.csdn.net/u012997373/article/details/24343947