题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1126
题意:从n个数中选出一些数,将选出的数分成两个集合,使得这两个集合的数字之和相等。求和最大为多少?
思路:f[i][j]表示前i个数构成两个差为j的集合,这两个集合的数字之和( 注意是这两个集合一起的数字之和)
最大为f[i][j]。则答案就是f[n][0]/2。具体实现时用滚动数组。。。真的不好想啊,看了别人的才知道的。。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define max(x,y) ((x)>(y)?(x):(y)) 5 #define min(x,y) ((x)<(y)?(x):(y)) 6 using namespace std; 7 8 int C,num=0; 9 int n,a[55],f[2][250005],sum; 10 11 int abs(int x) 12 { 13 return x>0?x:-x; 14 } 15 16 int DP() 17 { 18 memset(f,-1,sizeof(f)); 19 int cur=1,pre=0,i,j,k,t=0; 20 f[0][0]=0; 21 for(i=0;i<n;i++) 22 { 23 for(j=0;j<=t;j++) if(f[pre][j]!=-1) 24 { 25 f[cur][j]=max(f[cur][j],f[pre][j]); 26 k=abs(j-a[i]); 27 if(k<=sum) 28 { 29 f[cur][k]=max(f[cur][k],f[pre][j]+a[i]); 30 } 31 if(j+a[i]<=sum) 32 { 33 f[cur][j+a[i]]=max(f[cur][j+a[i]],f[pre][j]+a[i]); 34 } 35 } 36 cur^=1; 37 pre^=1; 38 t=min(t+a[i],sum); 39 } 40 return f[pre][0]>>1; 41 } 42 43 44 int main() 45 { 46 for(scanf("%d",&C);C--;) 47 { 48 scanf("%d",&n); 49 int i; 50 sum=0; 51 for(i=0;i<n;i++) scanf("%d",&a[i]),sum+=a[i]; 52 sum>>=1; 53 printf("Case %d: ",++num); 54 int ans=DP(); 55 if(ans==0) puts("impossible"); 56 else printf("%d\n",ans); 57 } 58 return 0; 59 }