题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5501
The Highest Mark
Accepts: 32
Submissions: 193
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
2045年的SD省队选拔,赛制和三十年前已是完全不同。一场比赛的比赛时间有 t 分钟,有 n 道题目。 第 i 道题目的初始分值为 Ai(Ai≤106) 分,之后每过一分钟这道题目的分值会减少 Bi 分,并且保证到比赛结束时分值不会减少为负值。比如,一个人在第 x 分钟结束时做出了第 i 道题目,那么他/她可以得到 Ai−Bi∗x 分。 若一名选手在第 x 分钟结束时做完了一道题目,则他/她可以在第 x+1 分钟开始时立即开始做另一道题目。 参加省队选拔的选手 dxy 具有绝佳的实力,他可以准确预测自己做每道题目所要花费的时间,做第 i 道需要花费 Ci(Ci≤t) 分钟。由于 dxy 非常神,他会做所有的题目。但是由于比赛时间有限,他可能无法做完所有的题目。他希望安排一个做题的顺序,在比赛结束之前得到尽量多的分数。
输入描述
第一行为一个正整数 T(T≤10),表示数据组数(n>200的数据不超过5组)。 对于每组数据,第一行为两个正整数 n(n≤1000) 和 t(t≤3000), 分别表示题目数量和比赛时间。接下来有 n 行,每行 3 个正整数依次表示 Ai,Bi,Ci,即此题的初始分值、每分钟减少的分值、dxy做这道题需要花费的时间。
输出描述
对于每组数据输出一行一个整数,代表dxy这场比赛最多能得多少分
输入样例
1 4 10 110 5 9 30 2 1 80 4 8 50 3 2
输出样例
88
Hint
dxy先做第二题,再做第一题,第一题得分为110−5∗(1+9)=60,第二题得分为30−2∗1=28,总得分为88,其他任何方案的得分都小于88
题解:
贪心+01背包。
贪心:
假设比赛时间无限大,每一道题目都能做完,那么你按照bi/ci由大到小排序的顺序完成所有题目会得到优成绩,
为什么呢?
任意两个相邻的题目i和j(i<j),先做i,你会多损失分数:ci*bj;先做j,你会多损失cj*bi,如果我们要先完成i,则有ci*bj<=cj*bi,即bi/ci>=bj/cj。
现在考虑有限时间内,如果最优解中的几个问题不是按照这个顺序来完成的,那么我们经过以上操作调整之后能使损失减小,从而得到更优解。
所以这个贪心是正确的。
dp:
贪心完之后,跑一遍01背包就可以了。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 6 const int maxn = 1010; 7 const int maxt = 3030; 8 9 struct Node { 10 int a, b, c; 11 bool operator < (const Node& tmp) { 12 return b*tmp.c>c*tmp.b; 13 } 14 }node[maxn]; 15 16 int N, T; 17 int dp[maxn][maxt]; 18 19 int main() { 20 int tc; 21 scanf("%d", &tc); 22 while (tc--) { 23 scanf("%d%d", &N, &T); 24 for (int i = 1; i <= N; i++) { 25 scanf("%d%d%d", &node[i].a, &node[i].b, &node[i].c); 26 } 27 sort(node + 1, node + N + 1); 28 /* 29 for(int i = 1; i <= N; i++) printf("%d ", node[i].a); 30 printf(" "); 31 */ 32 memset(dp[0], 0, sizeof(dp[0])); 33 for (int i = 1; i <= N; i++) { 34 for (int j = 0; j < node[i].c; j++) dp[i][j] = dp[i - 1][j]; 35 for (int j = node[i].c; j <= T; j++) { 36 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - node[i].c]+node[i].a-node[i].b*j); 37 } 38 } 39 int ans = 0; 40 for (int i = 0; i <= T; i++) ans = max(ans, dp[N][i]); 41 printf("%d ", ans); 42 } 43 return 0; 44 }
优化:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 7 const int maxn = 1010; 8 const int maxt = 3030; 9 10 struct Node { 11 int a, b, c; 12 bool operator < (const Node& tmp) { 13 return b*tmp.c>c*tmp.b; 14 } 15 }node[maxn]; 16 17 int N, T; 18 int dp[maxt]; 19 20 int main() { 21 int tc; 22 scanf("%d", &tc); 23 while (tc--) { 24 scanf("%d%d", &N, &T); 25 for (int i = 1; i <= N; i++) { 26 scanf("%d%d%d", &node[i].a, &node[i].b, &node[i].c); 27 } 28 sort(node + 1, node + N + 1); 29 memset(dp, 0, sizeof(dp)); 30 for (int i = 1; i <= N; i++) { 31 for (int j = T; j >= node[i].c; j--) { 32 dp[j] = max(dp[j], dp[j - node[i].c] + node[i].a - node[i].b*j); 33 } 34 } 35 int ans = 0; 36 for (int i = 0; i <= T; i++) ans = max(ans, dp[i]); 37 printf("%d ", ans); 38 } 39 return 0; 40 }