题目链接:https://jzoj.net/senior/#contest/show/2414/2
题目描述:
鸡腿是CZYZ的著名DS,但是不想追妹子的DS不是好GFS,所以鸡腿想通过表白来达到他追到妹子的目的!虽然你对鸡腿很无语,但是故事的设定是你帮助鸡腿找到了妹子,所以现在你必须帮助鸡腿安排表白来实现故事的结局 !
鸡腿想到了一个很高(sha)明(bi)的做法,那就是去找人来组成表白队伍来增强气势 !鸡腿有很多好基友来帮忙,鸡腿数了数一共有N个人。但是鸡腿觉得大家排成两队来比较好看,而且鸡腿经过计算,第一队N1个人,第二队N2个人是最佳的队伍。问题来了...有些好基友们虽然很好心但是可能造成不好的影响(形象猥琐),所以鸡腿就给每个人打了分。Q1i表示第i个好基友排到第一队里时的好影响,C1i表示第i个好基友排到第一队里时的不良影响,Q2i表示第i个好基友排到第二队里时的好影响,C2i表示第i个好基友排到第二队里时的不良影响。请给鸡腿一种安排使得Q的和与C的和的比值最大,给出最大值。
样例分析就略过了,其实我自己也没有仔细分析
下面直接开始讲题:
这道题的含义很好理解,有两个队列,每个人有各自的属性,我们要做的就是最优化
我们发现输出结果是个分数,考虑一下二分答案,发现若是答案t成立,则满足下面这个不等式:
∑(each k in Queue1)Q1k+∑(each k in Queue2)Q2k
----------------------------------------------------------- >=t
∑(each k in Queue1)C1k+∑(each k in Queue2)C2k
移项之后可以得到:
∑(each k in Queue1)(Q1k-C1k*t) +∑(each k in Queue2)(Q2k-C2k*t)>=0
于是这就是个01分数规划题
问题转化为构造最大的∑(each k in Queue1)(Q1k-C1k*t) +∑(each k in Queue2)(Q2k-C2k*t),怎么做呢?我们采用DP的方法
首先对于每一个人,我们计算出他的(q1i-c1i*t)-(q2i-c2i*t),注意中间是减号,为什么呢?我们考虑对最终答案的贡献,当然是越大越好,于是我们给这个新的属性(ne)从大到小排序
队列1从前向后取,队列二从后向前取。比如我们考虑大于0的情况,我们认为它是“靠前的”,那么显然放在队列1里面是比放在队列2里面好的,放在队列2里面就是负数了嘛
排序之后我们开始DP,用f[i][j] 表示前i 个人取了j 个进第一队的最大贡献,用g[i][j] 表示后i 个人取了j 个进第二队的最大贡献,最后用f[i][N1] +g[N-i][N2]来更新答案即可
若是满足大于等于0的条件,就l=mid,不满足r=mid
下面附上代码:
#include<bits/stdc++.h> using namespace std; const int inf=1e9+7; const int maxn=502; int n,n1,n2; double f[maxn][maxn],g[maxn][maxn]; struct NODE { double q1,c1,q2,c2,ne; }m[maxn],k[maxn]; bool cmp(NODE a,NODE b) {return a.ne>b.ne;} bool check(double t) { memset(f,-inf,sizeof(f)); memset(g,-inf,sizeof(g)); for (int i=1;i<=n;i++) k[i]=m[i]; for (int i=1;i<=n;i++) k[i].ne=(k[i].q1-k[i].c1*t)-(k[i].q2-k[i].c2*t); sort(k+1,k+1+n,cmp); f[1][1]=k[1].q1-k[1].c1*t; f[1][0]=0; for (int i=2;i<=n;i++) for (int j=0;j<=min(i,n1);j++) { f[i][j]=f[i-1][j]; f[i][j]=max(f[i][j],f[i-1][j-1]+k[i].q1-k[i].c1*t); } g[1][1]=k[n].q2-k[n].c2*t; g[1][0]=0; for (int i=2;i<=n;i++) for (int j=0;j<=min(i,n2);j++) { g[i][j]=g[i-1][j]; g[i][j]=max(g[i][j],g[i-1][j-1]+k[n-i+1].q2-k[n-i+1].c2*t); } double ans=-1000; for (int i=1;i<=n;i++) if (i>=n1&&n-i>=n2) if (f[i][n1]+g[n-i][n2]>ans) ans=f[i][n1]+g[n-i][n2]; if (ans>=0) return true;else return false; } int main() { //freopen("love.in","r",stdin); //freopen("love.out","w",stdout); scanf("%d%d%d",&n,&n1,&n2); for (int i=1;i<=n;i++) scanf("%lf%lf%lf%lf",&m[i].q1,&m[i].c1,&m[i].q2,&m[i].c2); double l=0,r=2000.0,mid; while (r-l>1e-12) { mid=(l+r)/2; if (check(mid)) l=mid; else r=mid; } printf("%.6f",mid); return 0; }