题目描述:
一个学校举行拔河比赛,所有人被分成了两组,每个人必须且只能够在其中一组,要求两个组的人数相差不能超过1,且两个组内所有人的体重加起来进可能地接近。
样例输入:
3
100
90
200
样例输出:
190 200
解题思路:
由于单个人最大重量比超过450,是一个极小的值,因此可以考虑将解(即重量)本身作为状态中的一个参量,将问题转换为判定性问题,通过判定解是否存在来找到最优解。
设 fi,j,kf_{i,j,k}fi,j,k 表示从 iii 个人中选 jjj 个人,组成总重量为 kkk 的组合是否可能存在。
由此可推知, fi,j,kf_{i,j,k}fi,j,k 的状态无非就是从这两种状态转移而来的:
若不选第 iii 个人,那么从 iii 个人中选 jjj 个人无非就转移成了 从 i−1i-1i−1 个人中选 jjj 个人,也就是 fi−1,j,kf_{i-1,j,k}fi−1,j,k。
若选第 iii 个人,那么 从 iii 个人中选 jjj 个人无非就转移成了从 i−1i-1i−1 个人中选 j−1j-1j−1 个人,组成重量为 k−wik-w_ik−wi 的组合,即 fi−1,j−1,k−wif_{i-1,j-1,k-w_i}fi−1,j−1,k−wi。
那么即可推出动态转移方程:
fi,j,k=fi−1,j,k or fi−1,j−1,k−wif_{i,j,k}=f_{i-1,j,k}\ \ \ or\ \ \ f_{i-1,j-1,k-w_i}fi,j,k=fi−1,j,k or fi−1,j−1,k−wi
这样子一来,最优解就是: 当 fn,n/2,i=truef_{n,n/2,i}=truefn,n/2,i=true 且 fn,n/2+n mod 2,i=turef_{n,n/2+n~mod~2,i}=turefn,n/2+n mod 2,i=ture 时的最小 iii
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;
}
这篇博客介绍了如何使用动态规划算法解决拔河比赛中分组的问题,确保两组人数相差不超过1且体重总和尽可能接近。通过设置状态转移方程,博主详细解析了代码实现过程,最终找到最优解。动态规划在这里起到了关键作用,优化了分组策略。

765

被折叠的 条评论
为什么被折叠?



