【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;
}

浙公网安备 33010602011771号