有 (n) 道题目,每道题分为小题目和大题目,只有做完了小题目才能做大题目,提交结果会在比赛结束后得知。其中,做第 (i) 个小题目需要 (ScoreSmall_i) 分钟,可以获得得分 (TimeSmall_i) ;做第 (i) 个大题目需要 (ScoreLarge_i) 分钟,可以获得得分 (TimeLarge_i) 。每个小题目可以一遍过,第 (i) 个大题目有 (ProbFail_i) 的几率错误,总罚时是 最后一次正确提交的时间。现在你有 (t) 分钟,求在这 (t) 分钟内你最多期望能够获得多少分,并且在满足得分最多的情况下使得罚时最小。
(nleq1000;t, TimeSmall_i, TimeLarge_iin[1, 1560]; ScoreSmall_i, ScoreLarge_iin[1, 10^9]; 0leq ProbFail_ileq 1)
dp,贪心,概率
背包可以解决最高得分,那么现在要找到一种最优的顺序使得罚时最低。
可以发现 (Small) 对罚时没有特殊影响,可以直接计算。假设需要解决两道题 (i, j) 的 (Large) ,令 (t_i=TimeLarge_i, p_i=ProbFail_i),若 (i) 排在 (j) 之前,罚时为 ((1-p_j)(t_i+t_j)+t_i(1-p_i)p_j)
化式子得,若 (i) 排在 (j) 之前的得分大于 (j) 排在 (i) 之前当且仅当 (t_i imes p_i/(1-p_i)>t_j imes p_j/(1-p_j))
按照这个规则排序即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long double db;
const db eps = 1e-15;
const int maxn = 2010;
int n, m;
db dp[maxn], val[maxn];
struct node {
int v1, v2, t1, t2; db p;
inline bool operator < (const node &o) const {
return t2 * p / (1 - p) < o.t2 * o.p / (1 - o.p);
}
} a[maxn];
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
double f;
scanf("%d %d %d %d %lf", &a[i].v1, &a[i].v2, &a[i].t1, &a[i].t2, &f);
a[i].p = f;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= m; i++) {
dp[i] = -1e18, val[i] = 1e18;
}
for (int i = 1; i <= n; i++) {
db p = a[i].p, tmp1, tmp2;
int v1 = a[i].v1, v2 = a[i].v2;
int t1 = a[i].t1, t2 = a[i].t2;
for (int j = m; j >= t1; j--) {
tmp1 = dp[j - t1] + v1, tmp2 = val[j - t1] + t1;
if (dp[j] < tmp1 || (fabs(dp[j] - tmp1) < eps && val[j] > tmp2)) {
dp[j] = tmp1, val[j] = tmp2;
}
if (j < t1 + t2) continue;
tmp2 = dp[j - t1 - t2] + v1 + (1 - p) * v2;
tmp1 = t1 + (1 - p) * (j - t1) + p * val[j - t1 - t2];
if (dp[j] < tmp2 || (fabs(dp[j] - tmp2) < eps && val[j] > tmp1)) {
dp[j] = tmp2, val[j] = tmp1;
}
}
}
int pos = 0;
for (int i = 1; i <= m; i++) {
if (dp[pos] < dp[i] || (fabs(dp[pos] - dp[i]) < eps && val[i] < val[pos])) {
pos = i;
}
}
printf("%.10lf %.10lf", (double) dp[pos], (double) val[pos]);
return 0;
}