POJ 2576 Tug of War 【二维01背包】

Tug of War

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 9236   Accepted: 2572

Description

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

The first line of input contains n 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 450. There are at most 100 people at the picnic.

Output

Your output will be a single line containing 2 numbers: the total weight of the people on one team, and the total weight of the people on the other team. If these numbers differ, give the lesser first.

扫描二维码关注公众号,回复: 2396915 查看本文章

Sample Input

3
100
90
200

Sample Output

190 200

Source

Waterloo local 2000.09.30

题意:将n个人分成两个部分,两边人数相等或者相差 1 ,并且两边体重总和相差最小;(这里要求的相差最小,其实就是两边的总重量尽可能的接近左右两边总重量的一半

思路:

做背包有关的题的时候,尽量把dp所表示的状态和dp的下标联系在一起,像一般的01背包问题,限制的条件就是背包的容量上限,所以一个一维背包dp【j】,j表示当前背包容量就可以解决问题了;这题有两个限制条件,一个是体重相差最小,一个是两边人数相差1或者相等,那么dp的状态尽可能的能够包含这两个限制条件,所以在这里设置一个二维dp【j】【k】,我们这里的dp可以只分析一边,因为知道了一边的总体重,另一半用sum 减去 求的这个总体重就是另一半的总体重了;这里的 j 表示的是左边的人数,k表示的左边的总体重;

下面代码的三重循环其实求的是,人数为 j 的时候的所有可能的重量组合,比如人数为 2的时候,可能有的组合有哪些,能组合得到的小于或者等于所有人数总重量一半的就记为 1 ,否则为0;最后得到人数为上限人数的dp后,再找最大的那个 k值,输出就是答案;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>

using namespace std;

#define Maxn 45010

int dp[105][Maxn],a[105],sum,book[Maxn];

int main(void)
{
    int n;
    while (scanf("%d",&n) != EOF) {
        sum = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(n == 1)  { printf("0 %d\n",a[1]); continue; }
        memset(dp,0,sizeof(dp));

        dp[0][0] = 1;
        int limit_people = (n+1)/2,limit_weight = sum/2;
        // 这里的人数上限 是(n+1)/2,如果总人数是偶数,那么结果和不 + 1的结果是一样的,
        //如果是奇数,那么求得的人数是人数最多的一边,
        // 因为两边的界限主要是用总重量来限制,如果上限的人数少一个,如果少掉的这个人的重量加上去不会超过
        //限制的重量,那么结果就会不对,宁可多算一个人都不能少算一个;
         for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= limit_people; ++j) {
                for (int k = limit_weight; k >= a[i]; --k) {
                    if(!dp[j][k] && dp[j-1][k-a[i]] && !book[k - a[i]]) { 
                            dp[j][k] = 1; 
                            if(j != limit_people) book[k] = 1;  // book的作用是为了避免同一个人的总量重复加
                    }
                    else book[k] = 0; // 只考虑当前人数的上一层的人数是否重复计算了,所以,上上层的book要及时清0
                }
            }
        }

        int ans;
        for (int i = limit_weight; i >= 1; --i)
            // i 是所有可能的重量组合中,总量不大于限制总重量的值,取最大的那个值,而且在两边不相等的情况下
            // 这个最大值可能是少 1 的一边,也可能是不少1的那边
        if(dp[limit_people][i] || dp[limit_people-1][i]) { ans = i; break; }
        if(ans > sum-ans) ans = sum-ans;

        printf("%d ",ans);
        printf("%d\n",sum-ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/godleaf/article/details/81099836
war