【SSL】1638拔河比赛
Time Limit:1000MS
Memory Limit:65536K
Description
一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,要求两个组的人数相差不能超过1,且两个组内的所有人体重加起来尽可能地接近。
Input
输入数据的第1行是一个n,表示参加拔河比赛的总人数,n<=100,接下来的n行表示第1到第n个人的体重,每个人的体重都是整数(1<=weight<=450)。
Output
输出数据应该包含两个整数:分别是两个组的所有人的体重和,用一个空格隔开。注意如果这两个数不相等,则请把小的放在前面输出。
Sample Input
3
100
90
200
Sample Output
190 200
思路
假设f[i,j,k]表示在前i个人中选j个人在一组,他们的重量之和等于k是否可能。
f[i,j,k]是bool型,其值为true代表有可能,false代表没有可能。
⑴第i个人没有被选中,此时就和从前面i-1个人中选出j个人的方案没区别,
所以f[i,j,k]=f[i-1,j,k]。
⑵第i个人被选中
f[i,j,k]=f[i-1,j-1,k-w[i]]
综上所述,可以得出:
f[i,j,k]=f[i-1,j,k] || f[i-1,j-1,k-w[i]]。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,a[101],sum;
bool f[51][22510];
void input()
{
int i;
memset(f,0,sizeof(f));
scanf("%d",&n);
for(sum=0,i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];//求和
}
f[0][0]=1;
return;
}
void DP()
{
int i,j,k;
for(i=1;i<=n;i++)
{
for(j=n/2;j>=0;j--)
{
for(k=j*450;k>=0;k--)
{
if (f[j][k])
f[j+1][k+a[i]]=1;//状态转移方程
}
}
}
return;
}
void output()
{
int i,ans,mn=1000000000;
for(i=0;i<=sum;i++)//找最小差
if (f[n/2][i])
{
if (abs(i-(sum-i))<mn)
{
mn=abs(i-(sum-i));
ans=i;
}
}
printf("%d %d",min(ans,sum-ans),max(ans,sum-ans));
return;
}
int main()
{
input();
DP();
output();
return 0;
}