lightoj-1147 - Tug of War(状压dp)

1147 - Tug of War
PDF (English) Statistics Forum
Time Limit: 4 second(s) Memory Limit: 32 MB
A tug of war is to be arranged at the local office picnic. For the tug of war, the picnickers must be divided into two teams. Each person must be on one team or the other; the number of people on the two teams must not differ by more than 1; the total weight of the people on each team should be as nearly equal as possible.

Input
Input starts with an integer T (≤ 100), denoting the number of test cases.

The first line of each case is a blank line. The next line of input contains an integer n (2 ≤ n ≤ 100), the number of people at the picnic. n lines follow. The first line gives the weight of person 1; the second the weight of person 2; and so on. Each weight is an integer between 1 and 100000. The summation of all the weights of the people in a case will not exceed 100000.

Output
For each case, print the case number and the total number weights of the people in two teams. If the weights differ, print the smaller weight first.

Sample Input
Output for Sample Input
2

3
100
90
200

4
10
15
17
20
Case 1: 190 200
Case 2: 30 32

 

解题思路: 这道题的大致思路就是01背包。 但是注意题目有一个条件,就是两队人数相差不过1。 所以不能只是单纯的01背包,我们算出最接近平均值的数的时候还要判断他否可以n/2 || n/2+1 个人组成。所以这道题在用01背包的基本模板计算时还要将可由几个人组成这个数的人数记录下来。一看需要记录状态的基本上就是状态压缩没有错了

因为n<=100, 所以要记录至多50位, 要用long long 来保存

ll  dp[w] = m ,w为重量,m为二进制记录。 m的第i位表示由i-1个人构成(因为初始化必须要dp[0]=1,但dp[0]虽然有1,但它是没有人构成他) ,则如果是110,则表示可由1个人构成,也可由2个人构成。 

综上可得转移方程 dp[w] = dp[w]|(dp[w-arr[i]]<<1);

#include<iostream>
#include<cstdio>
#include<cstring> 
#include<algorithm>
using namespace std;

typedef long long ll;
ll dp[50010];
ll arr[110];

bool judge(ll x,ll n){
    
    if(n%2==0){
        int num = n/2;
        return (x&(1ll<<num))!=0;
    }else{
        int num=n/2;
        return (x&(1ll<<num))!=0||(x&(1ll<<(num+1)))!=0;
    }
    
}

int main(){
    ll sum,T,n;
    scanf("%lld",&T);
    for(ll t=1;t<=T;t++){
        memset(dp,0,sizeof(dp));
        scanf("%lld",&n);
        sum = 0;
        for(ll i=0;i<n;i++){
            scanf("%lld",&arr[i]);
            sum+=arr[i];
        }
        dp[0] = 1;
        for(ll i=0;i<n;i++){
            for(ll j=sum/2;j>=arr[i];j--){
                dp[j] = dp[j]|(dp[j-arr[i]]<<1);
            }
        }
        for(ll i=sum/2;i>=0;i--){
            if(judge(dp[i],n)){
                printf("Case %lld: %lld %lld\n",t,i,sum-i);
                break;
            }
        }
        
    } 
    
} 

 

转载于:https://www.cnblogs.com/yuanshixingdan/p/5571586.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值