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; }