Tug of War
典型的01背包动态规划。其实鄙人是动态规划小菜鸟,只能是说知道有这么回事。但是比赛时这题没有做出来还是很郁闷。因为我的思想完全没有错,输出的数字我也想不到哪里还会错,一直WRONG ANSWER。我一直都觉得,要是我只是格式不对,会给我个PE,没想到这是送到UVA用虚拟账户测的,UVA里面没有PE的概念,只要答案和参考文件不是完全一样,就是WA。
有n个人,要分成2组,每个人有一个体重,要求两组人的总体重尽可能的接近。两个组的人数只差不得超过1.分别输出两个组分好后,人员总体重。总体重小的那个组先输出,中间隔一个空格。两组测试数据的测试结果之间有换行。
也就意味着出了最后一组外,其他的每组数据后面有换行。或说,出了第一组数据之外,其他数据数据的前面有空间(我就是错在这里,看题目不够仔细)。要是人的总数n是偶数,那么每组的人数都必须是n/2,否则,一组是n/2取整,一组是n/2取整后加1.
这个题目和刚入门的动归不一样,他求的不是最大值,也不是最小值,是最接近值。也就是说,要的是存在性。所以,我们可以求出在使用一定人数的情况下,可以组成的体重的总数的各种值。最后再把人数一定,找出最接近中间值的那个数即可。
动态转移方程: if(dp[j][k]) dp[j+1][k+wei[i]]=true;
dp[j][k]表示从所有人里面选出j个人能拼成k的总体重;
在j个人能拼出的总体重可以等于k时,那么我们加入第i个人,我们的总人数就是j+1了,这时候拼出的总体重就是k+wei[i]了;
在处理的时候,我们必须从后面往前面搜索k。否则,我们本次处理的前面得出的数据变化会影响到后面的处理。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; bool dp[55][22550]; //比较大,要放在堆里面,否则会爆栈 int wei[102]; //所有人的体重 int main() { int t; int n; int sum; int i,j,k; int f=0; cin>>t; while(t--) { sum=0; cin>>n; for(i=0; i<n; i++)cin>>wei[i],sum+=wei[i]; memset(dp,false,sizeof(dp)); dp[0][0]=true; //0个人的时候,总体重一定是0 for(i=0; i<n; i++) //动归打表 for(j=n/2; j>=0; j--) for(k=sum/2-wei[i]; k>=0; k--) if(dp[j][k]) dp[j+1][k+wei[i]]=true; int max; for(i=sum/2; i>=0; i--) //其中一组的总人数可以是n/2,总体重一定不大于sum/2 if(dp[n/2][i]) { max=i; break; } if(n%2!=0) //要是n为奇数,则该组人的总数可以为n/2+1 for(i=sum/2; i>=0; i--) if(dp[n/2+1][i]) { if(i>max)max=i; //求的是,在该组总体重不大于所有人的总体重的情况下,该组总体重的最大值(最接近sum/2) break; } if(f)cout<<endl; //案例间有空格 cout<<max<<" "<<sum-max<<endl; f=1; } return 0; }