题目(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[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[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--)
否则会超时.