Codeforces 1056F Write The Contest
题目链接
http://codeforces.com/contest/1056/problem/F
题目大意
有 $n$ 个题,每个题的难度为 $a_i$ ,分数为 $p_i$ ,你的最初的水平评级为 $s$ ,每做一道题需要 $s / a_i$ 时间,且每做一道题之前,你需要先花费 $10$ 分钟准备,准备后你的水平评级会变成 $0.9s$ 。你可以选择在任何时间进行学习,学习 $t$ 分钟会使水平评级增加 $C \cdot t$ 。问 $T$ 时间内最大的得分为多少。
题目分析
显然可知,先学习之后再一口气做题能使得获得的水平评分得到更充分的利用。且显然如果要做几道题,那么先做更难的题速度更快。因此先对所有的题目按照难度排序。之后考虑如何去求解最长时间。考虑到固定的几个题所用的时间是 $T = \sum\limits_{i=1}^{k}{(\frac{a_i}{(0.9) ^ {i} s} + 10)}$ ,可以将 $s$ 提取出来,有用时 $T = \frac{1}{s} \sum\limits_{i=1}^{k}{(\frac{a_i}{(0.9) ^ {i}})} + 10k$ 。
设 $T ^ {*} = \sum\limits_{i=1}^{k}{(\frac{a_i}{(0.9) ^ {i}})}$
因此可以设 $f[i][j][k]$ 为最后一个选的是 $i$ 题,一共选择了 $j$ 道题,且总分为 $k$ 且有 $T ^ {*}$ 最小,利用背包的简单优化可以将空间复杂度优化掉一维。最后对于每一种情况,使用三分法得到使得所花费时间最短的需要学习的时间,验证是否在题目要求时间内即可
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
int n, m, tc;
double c, t;
double a[105];
int p[105];
double f[105][1005];
pair <double, int> sr[105];
double s[105];
int read(){
int x;
scanf("%d", &x);
return x;
}
bool cmp(pair <double, int> x, pair <double, int> y){
return x > y;
}
double check(double x, double y){
return x + y / (1.0 + x * c);
}
double getn(double x){
double l = 0, r = t;
double lmid, rmid;
for(int i=1;i<=50;i++){
lmid = (2.0 * l + r) / 3.0;
rmid = (l + 2.0 * r) / 3.0;
if(check(lmid, x) > check(rmid, x)){
l = lmid;
}else{
r = rmid;
}
}
return check(l, x);
}
int solve(){
int i, j, k;
double inf = 1e18;
int ans = 0;
for(i=0;i<=n;i++){
for(j=0;j<=n*10;j++){
f[i][j] = inf;
}
}
f[0][0] = 0;
s[0] = 1.0;
for(i=1;i<=n;i++){
s[i] = s[i - 1] / 0.9;
}
for(i=1;i<=n;i++){
for(j=i;j>0;j--){
for(k=n*10-p[i];k>=0;k--){
f[j][k + p[i]] = min(f[j][k + p[i]], f[j - 1][k] + s[j] * a[i]);
}
}
}
for(i=1;i<=n;i++){
for(j=0;j<=n*10;j++){
if(i * 10 + getn(f[i][j]) <= t){
ans = max(ans, j);
}
}
}
printf("%d\n", ans);
return 0;
}
int main(){
int i, j;
tc = read();
while(tc--){
n = read();
scanf("%lf%lf", &c, &t);
for(i=1;i<=n;i++){
sr[i].first = read();
sr[i].second = read();
}
sort(sr + 1, sr + n + 1, cmp);
for(i=1;i<=n;i++){
a[i] = sr[i].first;
p[i] = sr[i].second;
}
solve();
}
return 0;
}