Description
Alice新开了一家公司,它的下面有两个项目,分别需要N1和N2个人来完成。现在有N个人前来应聘,于是Alice通过面试来决定他们中的哪些人会被录用。
Alice在面试中,会仔细考察他们能如何为公司的项目带来收益。她给每个人打了两个分值Q1和Q2,表示他加入第一个和第二项目分别能带来的收益值。同时,她也会仔细考察他们每个人的缺点,并且给每人打了另两个分值C1和C2,表示他们进入每个项目可能带来的负面效应。Alice心目中的最优决策是,在决定好录用哪些人以及每个人在哪个项目下工作之后,他们为公司带来的收益总和,除以他们为项目带来的负面效应总和,这个比值要最大。你能帮他计算出在最优决策下,这个比值为多少吗?
前来应聘的人数总是大于等于两个项目需求人数的总和,因此Alice一定会恰好招N1+N2个人,分配给第一个项目N1个人,分配给第二个项目N2个人,没有人会同时属于两个项目。
Input
输入文件包含多组测试数据。 第一行,给出一个整数T,为数据组数。接下来依次给出每组测试数据。 每组数据第一行为三个用空格隔开的整数N,N1,N2,表示前来应聘的人数,以及两个项目分别需要的人数。 接下来N行,每行是用空格隔开的四个整数Q1,C1,Q2,C2,依次表示每个人在第一个项目下的价值和负面效应,以及第二个项目下的价值和负面效应。 T ≤ 100 1 ≤ Q1, Q2 ≤ 2000 1 ≤ C1, C2 ≤ 50 0 < N1 + N2 ≤ N ≤ 50,
Output
对于每组测试数据,输出一行"Case #X: Y",其中X表示测试数据编号,Y表示最优决策下招募的人的价值总和与负面效应总和的比值,与正确答案的绝对误差不应超过10-6(其中表示次方,如:10^-6表示10的-6次方)。所有数据按读入顺序从1开始编号。
Sample Input 1
1
5 2 2
12 5 8 3
9 4 9 4
7 3 16 6
11 5 7 5
18 10 6 3
Sample Output 1
Case #1: 2.444444
源码
参考博客
用一个三阶的动态规划来做,dp[i][j][k]代表前i个人分j个人去做项目1,k个人去做项目2,具体解析看代码注释
#include <iostream>
#include <fstream>
#include <cstring>
#include <stdio.h>
#include <cmath>
using namespace std;
//前i个人j个人去做项目1,k个人去做项目2
//值为正面效益总和减去效益比乘上负面效应总和,若大于0则效益比要增大,小于0效益比要减小
//从而通过二分法来找出误差足够小的那个效益比
double dp[55][55][55];
struct person{
double q1,q2,c1,c2;
};
person data[55];
int n,n1,n2;
bool isok(double r)
{
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
{
if(j+k>i) break;
dp[i][j][k]=dp[i-1][j][k]; //初始化为第i个人不录用
if(j==0&&k==0) ;
else if(j==0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+data[i].q2-data[i].c2*r);
else if(k==0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+data[i].q1-data[i].c1*r);
else
{
//看是把第i个人安排去项目1值大还是项目2
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+data[i].q2-data[i].c2*r);
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+data[i].q1-data[i].c1*r);
}
}
if(dp[n][n1][n2]>0.0) return true;
else return false;
}
int main()
{
freopen("input/employee.txt","r",stdin);
int t;
cin>>t;
for(int cas=1;cas<=t;cas++)
{
cin>>n>>n1>>n2;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>data[i].q1>>data[i].c1>>data[i].q2>>data[i].c2;
}
for(int i=0;i<55;i++)
for(int j=0;j<55;j++)
for(int k=0;k<55;k++)
dp[i][j][k]=-999999.0; //初始化
dp[0][0][0]=0.0;
double l=0.0,r=2000.0; //二分找出误差足够小的效益比
while(r-l>0.000001)
{
double mid=(r+l)/2.0;
if(isok(mid)) l=mid; //说明效益比小了,所以dp>0.0,效益比要增大
else r=mid;
}
printf("Case #%d: %.6lf\n",cas,l); //必须是l,因为是r减去l的误差小于0.000001
}
return 0;
}