引言

训练赛遇到一个小学生的题,然后完全没思路.赛后才发现就是简单的邻项交换.

初学的时候一直对一种题很迷茫,就是排序一下就可以做的贪心,感觉这个简直就是瞎搞.

其实也是可以简单证明的.

题目:得分

  • 题目描述

现在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\n", ans);
    return 0;
}