题意: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; }