SRM 502 DIV1 medium TheProgrammingContestDivOne
問題概要
50問の問題がある。
それぞれの問題について
- 点数
- 解くためにかかる時間
- 1秒たつごとに減っていくその問題の点数
が与えられる。
使える時間T(最大100,000)が与えられる。
得られる点数を最大化せよ。
解法
貪欲+DP
この問題の難しいところは、解いたときの時間で問題の点数が変わってしまうところである。
ここで、時間内にある問題の集合Pを解くことができるとする。
その場合、問題A問題Bが含まれていて、
- 問題Aを解いてから問題Bを解く方が点数が高い場合
- 問題Bを解いてから問題Aを解く方が点数が高い場合
の両方の可能性がある。
線形的に両問題の点数は減っていくことから、このAとBの関係はどのタイミングでABを解いても成り立つ。
つまり、最適な場合の解く問題順は、事前に上記の基準でソートした問題の全体列の部分列になる。
解く順番さえ決まっていれば、
- 今何問目まで解くことを考慮したか。
- 今どれだけ時間を使ったか。
を状態にして、その状態の点数を最大化するDPすることが可能。
ソース
#include<iostream> #include<string> #include<algorithm> #include<vector> #define REP(i,b,n) for(int i=b;i<n;i++) #define rep(i,n) REP(i,0,n) using namespace std; typedef long long int lli; class Data{ public: int r, p, id; bool operator<(const Data &d)const{ return (double)r/p < (double)d.r/d.p; } }; class TheProgrammingContestDivOne { public: int find(int T, vector <int> maxPoints, vector <int> pointsPerMinute, vector <int> requiredTime) { int n = maxPoints.size(); vector<Data> D(n); rep(i, n){ D[i].r = requiredTime[i]; D[i].p = pointsPerMinute[i]; D[i].id = i; } sort(D.begin(), D.end()); lli dp[100001]; rep(i, 100001){ dp[i] = 0; } rep(i, n){ for(int j = T; j >= 0; j--){ if(j + D[i].r > T)continue; lli time = j + D[i].r; lli score = maxPoints[D[i].id] - time * D[i].p; dp[time] = max(dp[time], score + dp[j]); } } return *max_element(dp, dp + T + 1); } };