SSL 1638 拔河比赛

题目描述:

一个学校举行拔河比赛,所有人被分成了两组,每个人必须且只能够在其中一组,要求两个组的人数相差不能超过1,且两个组内所有人的体重加起来进可能地接近。

样例输入:

3
100
90
200

样例输出:

190 200


解题思路:

由于单个人最大重量比超过450,是一个极小的值,因此可以考虑将解(即重量)本身作为状态中的一个参量,将问题转换为判定性问题,通过判定解是否存在来找到最优解。

f i , j , k f_{i,j,k} fi,j,k 表示从 i i i 个人中选 j j j 个人,组成总重量为 k k k 的组合是否可能存在。
由此可推知, f i , j , k f_{i,j,k} fi,j,k 的状态无非就是从这两种状态转移而来的:
若不选第 i i i 个人,那么从 i i i 个人中选 j j j 个人无非就转移成了 从 i − 1 i-1 i1 个人中选 j j j 个人,也就是 f i − 1 , j , k f_{i-1,j,k} fi1,j,k
若选第 i i i 个人,那么 从 i i i 个人中选 j j j 个人无非就转移成了从 i − 1 i-1 i1 个人中选 j − 1 j-1 j1 个人,组成重量为 k − w i k-w_i kwi 的组合,即 f i − 1 , j − 1 , k − w i f_{i-1,j-1,k-w_i} fi1,j1,kwi
那么即可推出动态转移方程:
f i , j , k = f i − 1 , j , k     o r     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} fi,j,k=fi1,j,k   or   fi1,j1,kwi

这样子一来,最优解就是: 当 f n , n / 2 , i = t r u e f_{n,n/2,i}=true fn,n/2,i=true f n , n / 2 + n   m o d   2 , i = t u r e f_{n,n/2+n~mod~2,i}=ture fn,n/2+n mod 2,i=ture 时的最小 i i i


CODE:

#include <iostream>
#include <cmath>
using namespace std;
int n,w[110],sum=0,ans1,ans2=0;
bool f[110][45001]={false};  //由于f[i]的转移只与f[i-1]有关,因此可以省去一维
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>w[i],sum+=w[i];
	f[0][0]=true;  //初始条件
	for(int i=1;i<=n;i++)  //枚举总共人数
	  {
	  	for(int j=1;j<=i/2+1;j++)  //枚举每一组的人数
	  	  {
	  	  	for(int k=j*450;k>=max(j,w[i]);k--)  //枚举每一组人数可能的重量
	  	  	  {
	  	  	  	f[j][k]=f[j][k]||f[j-1][k-w[i]];  //状态转移
			  }
		  }
	  }
	ans1=(1e9);
	for(int i=n/2;i<=n/2*450;i++)  //找到最优解
	  if(f[n/2][i]&&f[n/2+n%2][sum-i])
	    {
	    	if(abs(ans1-ans2)>abs(i-sum+i))
	    	  ans1=i,ans2=sum-i;
		}
	cout<<min(ans1,ans2)<<" "<<max(ans1,ans2);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值