SSL·拔河比赛【DP】

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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值