CQYZOJ P1392 拔河问题

题目\(1\)

Description

  一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,且两个组内的所有人体重加起来尽可能地接近.

Input

  第\(1\)行是一个\(n\),表示参加拔河比赛的总人数,\(n<=100\),接下来的n行表示第\(1\)到第\(n\)个人的体重,每个人的体重都是整数\((1<=weight<=450)\)

Output

  包含两个整数:分别是两个组的所有人的体重和,用一个空格隔开。注意如果这两个数不相等,则请把小的放在前面输出。

Sample Input 1

3
100 90 200

Sample Output 1

190 200

Hint

\(n<=100,1<=weight<=450\)

模型

\(0-1\)背包

解法

转换成成一个花费\(=\)价值的\(0-1\)背包问题,记\(F[i][j]\)为用前\(i\)个物品,总代价\(<=j\)能取得的最大价值,可得状态转移方程:

\[F[i][j]=max(F[i][j],F[i][j]-w[i]]+w[i]) \]

最后答案即为\(F[N][Sum/2]\),其中\(Sum=\sum_{i=1}^Nw[i]\).

实际代码中,还可以使用滚动数组来优化空间.

代码

#include<bits/stdc++.h>
using namespace std;

#define MaxN 105
#define Maxw 45005
int w[MaxN],N;
int F[Maxw];
int Tx=0;

int main()
{
	cin>>N;
	for(int i=1;i<=N;i++)
	{
		cin>>w[i];
		Tx+=w[i];
	}
	for(int i=1;i<=N;i++)
		for(int P=Tx;P;P--)
			if(P-w[i]>=0)
				F[P]=max(F[P],F[P-w[i]]+w[i]);
	cout<<F[Tx/2]<<" "<<Tx-F[Tx/2]<<endl;
	return 0;
}

题目\(2\)

Description

  一个学校举行拔河比赛,所有的人被分成了两组,每个人必须(且只能够)在其中的一组,两个队伍的人数之差不能超过1,且两个组内的所有人体重加起来尽可能地接近.

Input

  第\(1\)行是一个\(n\),表示参加拔河比赛的总人数,\(n<=100\),接下来的n行表示第\(1\)到第\(n\)个人的体重,每个人的体重都是整数\((1<=weight<=450)\)

Output

  包含两个整数:分别是两个组的所有人的体重和,用一个空格隔开。注意如果这两个数不相等,则请把小的放在前面输出。

Sample Input 1

3
100 90 200

Sample Output 1

190 200

Hint

\(n<=100,1<=weight<=450\)

模型

\(0-1\)背包

解法

同样转换成成一个花费\(=\)价值的\(0-1\)背包问题,记\(F[i][j][k]\)为在前\(i\)个物品中选择\(k\)个,总代价\(<=j\)能取得的最大价值.可得状态转移方程:

\[F[i][j][k]=max(F[i][j][k],F[i-1][j-1][k-w[i]]+w[i]) \]

最终答案即为\(F[N][N/2][Sum]\),其中\(Sum=\sum_{i=1}^Nw[i]\).

同样可以采用滚动数组优化,还要注意初始化边界.

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f
#define MaxN 105
#define Maxw 45005
int w[MaxN],N;
int F[MaxN][Maxw];
int Tx=0;

int main()
{
	cin>>N;
	for(int i=1;i<=N;i++)
	{
		cin>>w[i];
		Tx+=w[i];
	}
	memset(F,-INF,sizeof(F));
	for(int i=0;i<=Tx>>1;i++)
		F[0][i]=0;
	for(int i=1;i<=N;i++)
		for(int j=i;j>=1;j--)
			for(int P=Tx>>1;P>=w[i];P--)
					F[j][P]=max(F[j][P],F[j-1][P-w[i]]+w[i]);
					
	int Ans=F[N>>1][Tx>>1];
	if(N%2)
		Ans=max(Ans,F[(N>>1)+1][Tx>>1]);		
	cout<<Ans<<" "<<Tx-Ans<<endl;
	return 0;
}

还要注意,本题中第三重循环必须从\(Sum/2\)开始,即代码中的

for(int P=Tx>>1;P>=w[i];P--)

否则会超时.

posted @ 2019-07-11 20:13  TaylorSwift13  阅读(462)  评论(0编辑  收藏  举报