light oj 1147 背包dp+状态压缩 好题

题意:n个物品(2<=n<=100),将n个物品平均分成2堆(n为奇数,两堆个数差1),要你让这两堆的差值尽最小,输出两堆的重量,物品总重量不超过100000

思考:如何保存两堆物品个数差值是这题的关键。如果没有平均分,那么我们假设sum为总重量, 搞个sum/2的背包去装,dp结束以后就O(sum/2)扫一遍。 

分析: 但这里要求平均分,我们想想没有平均分的做法dp里面的值是bool型的,这题我们可以扩增dp里面的值,来保存我装满了j的背包是由几个物品构成的,当然要记录所有的组合情况,考虑到n只有100,对半分后就是50, dp开个long long,  用一个二进制数表示   装满了j的背包是由几个物品构成的, 比方说由3个物品构成,那么dp的第三位二进制位为1,否则为0, 然后就可以转移了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
int n, a[101];
int sum, m, s;
ll dp[50004], mask;
int main() {
    int i, j, cas, ca = 1;
    scanf("%d", &cas);
    while(cas--) {
        scanf("%d", &n);
        sum = 0;
        mask = (1LL<<((n>>1)+2))-1;
        for(i = 0; i < n; i++)
            scanf("%d", &a[i]), sum += a[i];
        m = sum >> 1;
        for(i = 0; i <= m; i++) dp[i] = 0;
        dp[0] = 1;
        for(i = 0; i < n; i++)
            for(j = m; j >= a[i]; j--) if(dp[j-a[i]])
                dp[j] |= (dp[j-a[i]]<<1)&mask;
        printf("Case %d: ", ca++);
        ll x = 1LL << (n/2);
        ll y = 1LL << ((n+1)/2);
        for(i = m; i >= 0; i--)
            if(dp[i]&x || dp[i]&y) {
                printf("%d %d\n", i, sum-i);
                break;
            }
 
    }
    return 0;
}



你可能感兴趣的:(light oj 1147 背包dp+状态压缩 好题)