【luogu CF277D】Google Code Jam(DP)

Google Code Jam

题目链接:luogu CF277D

题目大意

你有 T 的时间,要做 n 道题,每道题有部分分的分数以及它需要的时间,在变成正解能多出的分数以及需要的时间以及正解写挂的概率。
如果写挂了可以选择不提交,问你期望最大分数以及在这个最大分数下的期望最小罚时。
罚时是指最后一次提交代码的时间。

思路

首先不考虑罚时,那就是一个普通的背包,直接每个物品三种情况要么不写,要么写暴力,要么再试着写正解。
那正解额外多的期望分数就是 \(val2*p_i\)(这里 \(p_i\) 是不挂的概率)

然后考虑这个罚时,那我们肯定是先把要写的暴力都写了,再用某种顺序来写正解。
而且要注意的是这个罚时不一定只跟最后一个写的正解有关,它可能后面的若干个正解都挂了,所以你要安排出一个顺序而不是单纯确定最后一个。

考虑排序的比较,那我们其实就是要让如果寄了,那个距离结束的时间要尽可能的长。
那就直接用这个比:
\((1 - x.p)(1 - y.p)y.ti1 + (1 - y.p)y.ti2 > (1 - y.p)(1 - x.p)x.ti1 + (1 - x.p)x.ti2\)
然后化简一下:
\((1- x.p)x.ti2 / x.p < (1- y.p)y.ti2 / y.p\)

然后注意一下卡精度就好。

代码

#include<cmath>
#include<cstdio>
#include<algorithm>
 
using namespace std;
 
const int N = 1565;
const long double eps = 1e-15;
int n, t;
long double f[N], g[N];//g:f中ti2*(1-p)最大的 
struct node {
    int va1, va2, ti1, ti2; long double p;
}a[N];

bool same(long double x, long double y) {
    return fabs(x - y) < eps;
}
 
bool cmp(node x, node y) {
//    return (1.0 - x.p) * (1.0 - y.p) * y.ti1 + (1.0 - y.p) * y.ti2 > (1.0 - y.p) * (1.0 - x.p) * x.ti1 + (1.0 - x.p) * x.ti2;
	return (1.0 - x.p) * x.ti2 / x.p < (1.0 - y.p) * y.ti2 / y.p;
}
 
int main() {
    scanf("%d %d", &n, &t);
    for (int i = 1; i <= n; i++) {
        int va1, va2, ti1, ti2; double p;
        scanf("%d %d %d %d", &va1, &va2, &ti1, &ti2); scanf("%lf", &p); p = 1.0 - p;
        a[i] = (node){va1, va2, ti1, ti2, p};
    }
     
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= t; i++) f[i] = -1.0, g[i] = 0.0;
    for (int i = 1; i <= n; i++) {
        int va1 = a[i].va1, va2 = a[i].va2, ti1 = a[i].ti1, ti2 = a[i].ti2;
        long double p = a[i].p;
        //ti1->va1
        //ti2->va2*p, ti2*(1-p)
        for (int j = t; j >= 0; j--) {
            if (j + ti1 <= t) {
                if (f[j + ti1] < f[j] + va1 || (same(f[j + ti1], f[j] + va1) && g[j] > g[j + ti1])) {
                    f[j + ti1] = f[j] + va1; g[j + ti1] = g[j];
                }
            }
            if (j + ti1 + ti2 <= t) {
                if (f[j + ti1 + ti2] < f[j] + va1 + p * va2 || (same(f[j + ti1 + ti2], f[j] + va1 + p * va2) && (1.0 - p) * (g[j] + ti2) > g[j + ti1 + ti2])) {
                    f[j + ti1 + ti2] = f[j] + va1 + p * va2; g[j + ti1 + ti2] = (1.0 - p) * (g[j] + ti2);
                }
            }
        }
    }
     
    long double ans1 = -1.0, ans2 = 0.0;
    for (int i = 0; i <= t; i++) {
        if (f[i] > ans1 || (same(f[i], ans1) && 1.0 * i - g[i] < ans2)) {
            ans1 = f[i]; ans2 = 1.0 * i - g[i];
        }
    }
    printf("%.10lf %.10lf", (double)ans1, (double)ans2);
     
    return 0;
}
posted @ 2022-10-25 13:10  あおいSakura  阅读(43)  评论(0)    收藏  举报