lightoj 1126 - Building Twin Towers DP

给定n个数字,然后选择一些数字分成两堆,这两堆的和相等,求最大的和...

首先求一下所有数字的和...那么最后结果肯定小于等于sum/2。

然后范围50W...那么肯定滚动数组先,然后两数字的差当做第二维...

两数字的差要修正一下范围100W,遍历也要100W..然而可以并不需要,因为我们可以dp[i][j]代表选择前i个数字后两数字相差为j的最大值,那么最大值减去差值就是最小值。如果最小值加了某个数字大于最大值,可以换过来...

对于一个数字来言,可以放在第一个数字(不能爆sum),放在第二个数字(超过第一个数字or没超过)或者不选...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007
#define inf 0x3f3f3f3f
int dp[2][500200];
int a[60];
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n,sum=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        sum=sum/2;
        memset(dp,-1,sizeof(dp));
        dp[0][0]=0;
        int s=1;
        for(int i=0;i<n;i++)
        {
            s^=1;
            for(int j=0;j<=sum;j++)
            {
                dp[s^1][j]=max(dp[s][j],dp[s^1][j]);
                if(dp[s][j]==-1) continue;
                if(j+a[i]<=sum)
                    dp[s^1][j+a[i]]=max(dp[s^1][j+a[i]],dp[s][j]+a[i]);//给大的数字加上大的...爆了sum就没意思了..
                if(j>=a[i])
                    dp[s^1][j-a[i]]=max(dp[s^1][j-a[i]],dp[s][j]);//给小的数字加,没超过大的
                else
                    dp[s^1][a[i]-j]=max(dp[s^1][a[i]-j],dp[s][j]+a[i]-j);//给小的数字加完超过大的了
            }
        }
        printf("Case %d: ",cas);
        if(dp[s^1][0]==0) printf("impossible\n");
        else printf("%d\n",dp[s^1][0]);
    }
    return 0;
}


你可能感兴趣的:(dp,乱搞,lightoj)