拔河比赛【DP】 SSL 1638
Description–
一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,要求两个组的人数相差不能超过1,且两个组内的所有人体重加起来尽可能地接近。
Input–
输入数据的第1行是一个n,表示参加拔河比赛的总人数,n<=100,接下来的n行表示第1到第n个人的体重,每个人的体重都是整数(1<=weight<=450)。
Output–
输出数据应该包含两个整数:分别是两个组的所有人的体重和,用一个空格隔开。注意如果这两个数不相等,则请把小的放在前面输出。
Sample Input–
3
100
90
200
Sample Output–
190 200
解题思路–
假设w[i]表示第i个人的体重。f [i][j][k]表示在前i个人中选j个人在一组,他们的重量之和等于k是否可能。
从前i个人中选出j个人的方案,不外乎两种情况:
- 第i个人没有被选中,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] or f[i-1][j-1][k-w[i]]。
因为i只与i-1有关,所以在具体实现的时候,可以把第一维省略掉。就变成f[j][k]
操作时,要注意控制j与k的范围(0<=j<=i/2,0<=k<=450*j),否则有可能超时。
代码zy片段写法1:
for(i=1;i<=n;i++)
for(j=n2-1;j>=0;j--)
for(k=smr*j;k>=0;k--)
if (m[j][k]) m[j+1][k+a[i]]=1
代码zy片段写法2:
for (int i=1;i<=n;++i)
for (int v=s[i];v>=a[i];--v)
for (int j=1;j<=n/2;++j)
f[v][j]=f[v][j]||f[v-a[i]][j-1];
代码–
#include<cstdio>
using namespace std;
int min(int a,int b)
{
if (a<b) return a;
return b;
}
int max(int a,int b)
{
if (a>b) return a;
return b;
}
int s,m,n,w[110],f[110][45010];
int main()
{
scanf("%d",&n);
f[0][0]=1; //初始化
for (int i=1;i<=n;++i)
scanf("%d",&w[i]),m+=w[i]; //用m累加w[]的值
for (int i=1;i<=n;++i)
for (int j=min(n/2,i-1);j>=0;--j)
for (int k=0;k<=j*450;++k)
if (f[j][k]) f[j+1][k+w[i]]=1;
s=m,m/=2; //s用来处理m是单数的情况(本质则不是)
for (int i=0;i<=25000;++i)
{
if (f[n/2][m+i])
{
printf("%d %d",min(s-(m+i),m+i),max(s-(m+i),m+i)); //总体重-1组的体重=另1组的体重
break;
}
if (f[n/2][m-i])
{
printf("%d %d",min(m-i,s-(m-i)),max(m-i,s-(m-i))); //同上
break;
}
}
return 0;
}