jzoj3454 表白(love)解题报告(01分数规划+DP)

题目链接: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的和的比值最大,给出最大值。 

Input

第一行给出三个整数N、N1、N2。 

第2到N+1行,每行四个整数Q1,C1,Q2,C2。 

Output

一行输出一个小数d表示最优化比例是d(保留6位小数) 

 

Sample Input

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

2.444444

 

样例分析就略过了,其实我自己也没有仔细分析

下面直接开始讲题:

这道题的含义很好理解,有两个队列,每个人有各自的属性,我们要做的就是最优化

我们发现输出结果是个分数,考虑一下二分答案,发现若是答案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;
}
原文地址:https://www.cnblogs.com/xxzh/p/9274214.html