1.拔河问题
又是0/1背包问题的变式 ~
人懒直接转载了...别人说的也确实很好 ~
注:以下转载,有自己的稍微改动,侵删。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题描述:n 个人参加拔河比赛,每个人有自己的重量,现在需要把他们分成两组进行比赛,每个人属于其中的一个组。为使比赛公平,求使得两组重量差最小的分配。
分析问题:明显的,我前面已经说明,是要采用0-1背包来求解。
问题中要求:最终的队员分配要使得两组队的重量差最小,那么如何才可以实现呢?我们可以这样考虑:
假设所有队员的总质量为sum,那么两队A,B的平均质量 average = sum/2.
那么假如队员分配之后,A队的总重量“接近” average,那么间接的,B队的总重量也会”接近“average。这里的“接近”指的是队中的总重量 大于 或者 小于(当然也有可能是相等)average某一个值,这个值相对来说是小的。那么这样分配的最终结果就会呈现出:两组队的重量差最小。
那么现在我们就可以采用 0-1背包来解决这个问题。
也就是说,这个背包的大小是 average,那么在可供选择的队员中选择,使到,选择的队员总质量不超过 <= average (也就是“接近”average),那么这样就可以使选择的队员总质量达到最接近average,那么间接的,剩下的所有队员都给另一个,其总质量就不小于 >= average。这样两队之间的质量差也就可以最小了。
问题升级:关于拔河比赛,还有一种问题描述:
n 个人参加拔河比赛,每个人有自己的重量,现在需要把他们分成两组进行比赛,每个人属于其中的一个组,两组的人员个数相差不能超过1。为使比赛公平,求使得两组重量差最小的分配。
其实就是多了一个限制条件,两组队员之间的队员人数不能超过1。看到上面的运行例子,可以发现,两队之间的队员分别是 A:2 ;B : 4。所以明显的,不符合题目的要求,所以代码要改进。
那么怎么考虑呢?
首先,我们要肯定的是,上面使用到的 0-1背包策略都是没有问题了,这个问题的求解也是这个过程。而且,这样求解出来的质量差是最小的。如果添加了这个限制条件,那么质量差不一定是最小的了,这个应该是可以理解到的。那么对于这个限制条件怎么实现呢?
我们可以这样想:如果经过上面的求解过程出来的分配结果刚好 A队和B队之间的队员数量相查不超过1,那么最好了,不用额外的处理。那么假如恰好二者相差超过1,例如上面的例子:A队有2人;B队有4人。那么怎么样才可以实现两队之间队员相差1呢?明显要从B队中选一人给A队,这样两队都是3人。那么在B队中选择哪一名队员给A呢?当然要选择B队中重量最小的,这样才可以保证两组重量差最小。(这里可以保证该解是正确的,可以用反证法)
好了,解决的策略出来了,就是要在上面代码的基础上添加一个人数的判断,如果人数相差超过1,就从多人的一队中选择一名重量最小的给队员数少的一队,直到两队的人数相差不超过1.
考虑到是要从一个队中选择一个重量最小的出来,前面的代码使用的是stack来装载队员,明显这里不是很适合,那么我们可以选用优先队列来装载队员,那么在选重量最小的队员出来的时候,优先队列就很方便了。
#include <iostream>
#include <queue>
#include <cmath>
using namespace std;
//定义一个拔河队员的结构体
typedef struct people
{
int number; //队员编号,从1开始
int weight; //队员重量
char team;
//为了使得两组的人员个数相差不能超过1,所以增加一个变量用于确定这名队员是A还是B。
//确定的规则是,如果一方较多,那么找出一个质量最小的给另一个对,再看看两个队直接的人数是否相差1;
//如果不是,那么继续选一个质量最小的给另一队。
friend bool operator < (people p1,people p2)
{
return p1.weight > p2.weight; //重量小的优先
}
} peoples;
const int N = 100;
const int W = 5000;
int arr[N+1][W+1];
//二维表用于记录,其中行下标表示人的编号,列下标表示背包质量,也就是每一队的质量,假定不会超过5000.
int getMax(int a,int b)
{
return a>=b?a:b;
}
int main()
{
cout<<"输入人数:";
int num;
cin>>num;
peoples *players = new peoples [num+1]; //创建num名队员,队员编号从1开始
int sum = 0; //计算所有人的总质量
cout<<"输入每一个人的质量:";
//输入每一个人的质量
for(int i=1; i<=num; i++)
{
int w = 0;
cin>>w;
players[i].number = i;//队员编号
players[i].weight = w; //队员的重量
players[i].team = 'B'; //假设都是B队的队员
sum+=w;
}
int average = sum/2;
//初始化二维表中的第一行
for(int k=0; k<=W; k++) arr[0][k] = 0;
//初始化二维表中的第一列
for(int k=0; k<=N; k++) arr[k][0] = 0;
//逐行进行填表
for(int i=1; i<=num; i++) //人的编号从 1~N
{
for(int j=1; j<=average; j++) //每一队质量从 1~average
{
arr[i][j] = arr[i-1][j];
if(players[i].weight <= j)
{
arr[i][j] = getMax(arr[i][j],arr[i-1][j-players[i].weight]+players[i].weight);
}
}
}
//输出当前用到的二维表
// for(int i=0; i<=num; i++) //物品编号从 1~N
// {
// for(int j=0; j<=average; j++) //背包容量从 1~W
// {
// cout<<arr[i][j]<<" ";
// }
// cout<<endl;
// }
int remainspace = average;
//选出A队的队员
for(int i=num; i>=1; i--)
{
if (remainspace >= players[i].weight)
{
if ((arr[i][remainspace]-arr[i-1][remainspace-players[i].weight])==players[i].weight)
{
players[i].team = 'A'; //选出是A队中的队员
remainspace = remainspace - players[i].weight;
}
}
}
//第一轮分配队员的结果
for(int i = 1;i<=num;i++)
{
cout<<"Team:"<<players[i].team<<" "<<"Number:"<<players[i].number<<" Weight:"<<players[i].weight<<endl;
}
priority_queue<peoples> playerA; //创建队伍A
priority_queue<peoples> playerB; //创建队伍B
//统计A队和B队的队员
for(int i = 1; i<= num; i++)
{
if(players[i].team == 'A')
{
playerA.push(players[i]);
}
else
{
playerB.push(players[i]);
}
}
int countA = playerA.size();
int countB = playerB.size();
//为了确保A B两队人数相差不超过1,进行调整
//如果A B队的队员人数相差不是1的处理
while(abs(countA-countB)>1)
{
//如果A队的队员比较多,那么就要从A队中选出一个重量最小的给B队
if(countA>countB)
{
playerB.push(playerA.top());
playerA.pop();
countA--;
countB++;
}
//如果B队的队员比较多,那么就要从B队中选出一个重量最小的给A队
else if(countA<countB)
{
playerA.push(playerB.top());
playerB.pop();
countA++;
countB--;
}
}
cout<<"A:"<<endl;
int sumA = 0;
while(!playerA.empty())
{
cout<<"Number:"<<playerA.top().number<<" Weight:"<<playerA.top().weight<<endl;
sumA+=playerA.top().weight;
playerA.pop();
}
cout<<"A team sum of weight = "<<sumA<<endl;
cout<<endl;
//输出B队成员
cout<<"B:"<<endl;
int sumB = 0;
while(!playerB.empty())
{
cout<<"Number:"<<playerB.top().number<<" Weight:"<<playerB.top().weight<<endl;
sumB+=playerB.top().weight;
playerB.pop();
}
cout<<"B team sum of weight = "<<sumB<<endl;
cout<<endl<<"两队之间的重量差:"<<abs(sumA-sumB)<<endl;
delete [] players;
return 0;
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------