引言
训练赛遇到一个小学生的题,然后完全没思路.赛后才发现就是简单的邻项交换.
初学的时候一直对一种题很迷茫,就是排序一下就可以做的贪心,感觉这个简直就是瞎搞.
其实也是可以简单证明的.
题目:得分
- 题目描述
现在zql手上有 N 道题,他总共有 T 的时间来完成他们中的一些或全部。每道题有一个完成所需时间 t[i]和一个难度系数 c[i]。如果 zql 在剩余 x 个单位时间的时候开始做题i,并且能够完成,那么总分加上 x*c[i]。现在 zql 要从这 N 道题中选出一些在T个单位时间内完成,并且按照某种顺序依次完成它们(zql 每个单位时间只能做一道题,并且一旦他决定做某题就会一直做直到做完),那么他最多能够拿到多少分呢?输入总共包括 N+1 行,第一行,两个用空格隔开的正整数 N 和 T,分别表示题目总数和总时间。第 2 到 N+1 行,每行包含两个正整数,第 i+1 行的两个正整数分别表示 t[i]和 c[i].
- 输出
共包括 1 行。 一行一个整数,表示最大能够得到的分数。
样例输入
【样例1】
3 10
2 1
8 9
2 5
样例输出
样例1
122
- 提示
样例1解释:最优方案为在剩余 10 个单位时间的时候开始做第 3 题(需要 2 个单位时间),剩余 8 个单位时间的时候开始做第 2 题(需要 8 个单位时间),总得 分为 10×5+(10-2)×9=122。
分析
设相邻两项,一个为(A(v_1,t_1)),另一个为(B(v_2,t_2)),当前时间为(T)
若A在B左边产生的价值:(A=v_1*T+v_2(T-t_1)=v_1*T+v_2*t-v_2*t_1)
若B在A左边产生的价值:(B=v_2*T+v_1(T-t_2)=v_1*T+v_2*t-v_1*t_2)
若(A>B),则(v_1*t_2>v_2*t_1)
所以按照上面的分析排序之后得到的就是优先方案,但是并不是排序之后for循环就好了
还有一个总的时间限制.这就意味着有些看上去价值很高,实际上塞不进去或者对后面影响很大,这就需要DP
#include<bits/stdc++.h>
const int mod = 1e9 + 7;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 3500;
struct node {
int v, t;
} s[maxn];
bool cmp(node a, node b) {
return a.v * b.t > b.v * a.t;
}
int dp[10010];
int main() {
int n, T;
scanf("%d%d", &n, &T);
for (int i = 1; i <= n; ++i) scanf("%d%d", &s[i].t, &s[i].v);
sort(s + 1, s + 1 + n, cmp);
for (int i = 1; i <= n; ++i) {
for (int j = T; j >= s[i].t; --j) {
dp[j] = max(dp[j], dp[j - s[i].t] + (T - j + s[i].t) * s[i].v);
}
}
int ans = 0;
for (int i = 0; i <= T; ++i) {
ans = max(ans, dp[i]);
}
printf("%d
", ans);
return 0;
}