题目:
http://poj.org/problem?id=2576
题意:
有n个人参加分为两队参加拔河比赛,每个人有一个体重。要求两队的体重和只差最小,且人数相差不得超过1,分别求两队的总体重,先输出较小的
思路:
分组背包问题。题目有两个限制因素:体重和人数。设总体重为sum,那么应该求n/2(n为奇数求n/2和n/2+1)的人在不超过sum/2时所能达到的最大体重。 定义 dp[i][j][k] 为前i个人体重为选出k个人所达到的不超过j的最大体重,得状态转移方程为 dp[i][j][k]=max(dp[i−1][j][k],dp[i−1][j−w][k−1]+v) ,可以优化空间把第一维优化掉。注意初始应该把dp数组初始化为-1,把dp[0][0][0]初始化为0,这是因为当dp[i-1][j-w][k-1]这个状态没有从其他状态到达时,那么实际这个状态就只有1个人而不是k-1个人,于是状态就错了,在从这里转移的话, 导致后面的都出错,所以不能从这里转移到dp[i][j][k],所以初始只有dp[0][0][0]是合法的状态。另外n为奇数时,两队人数不同, 无法确定哪队能达到不超过体重一半的最大值,所以两队都要求,取其中的较大值。代码附数据一组,答案为6 12,很多代码过不了
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int v[N];
int dp[N*250][N];
int work(int n, int m, int r)
{
memset(dp, -1, sizeof dp);
dp[0][0] = 0;
for(int i = 1; i <= n; i++)
for(int j = m; j >= v[i]; j--)
for(int k = r; k >= 1; k--)
if(dp[j-v[i]][k-1] != -1)
dp[j][k] = max(dp[j][k], dp[j-v[i]][k-1] + v[i]);
for(int i = m; i >= 0; i--)
{
if(n & 1)
{
if(dp[i][r-1] != -1 || dp[i][r] != -1) return i;
}
else
{
if(dp[i][r-1] != -1) return i;
}
}
}
int main()
{
int n;
while(~ scanf("%d", &n))
{
int sum = 0;
for(int i = 1; i <= n; i++) scanf("%d", &v[i]), sum += v[i];
int res = work(n, sum/2, n/2+1);
printf("%d %d\n", min(res, sum-res), max(res, sum-res));
}
return 0;
}
//4
//9 3 3 3