动态规划-codeforces

题目描述 

牛牛正在打一场CF
比赛时间为T分钟,有N道题,可以在比赛时间内的任意时间提交代码
第i道题的分数为maxPoints[i],题目的分数随着比赛的进行,每分钟减少pointsPerMinute[i]
这是一场比较dark的Cf,分数可能减成负数
已知第i道题需要花费 requiredTime[i] 的时间解决
请问最多可以得到多少分

输入描述:

第一行输入两个整数N,T (1 ≤ N ≤ 50, 1 ≤ T ≤ 100000)
第二行输入n个整数maxPoints[i]
第三行输入n个整数pointsPerMinute[i]
第四行输入n个整数requiredTime[i]
1 ≤ maxPoints[i],pointsPerMinute[i],requiredTime[i] ≤ 100000

输出描述:

输出一个整数
示例1

输入

复制
1 74
502
2
47

输出

复制
408
示例2

输入

复制
2 40000
100000 100000
1 100000
50000 30000

输出

复制
0
示例3

输入

复制
3 75
250 500 1000
2 4 8
25 25 25

输出

复制
1200
示例4

输入

复制
3 30
100 100 100000
1 1 100
15 15 30

输出

复制
97000

思路:首先,题目有分数和完成时间,两个属性。且有时间限制。可以得出是一个01背包问题。
但是由于题目的分数是在变化的,所以它不同于简单的背包问题。以此要算出它的性价比进行排序。先做能得分的且得分高的
假设分数为f,每分钟减少p,花费时间是t.有两个题目i,j.假设写题时间是t1+t2;
如果先写i题Ti=fi-pi*ti+fj-pj*(ti+tj)=fi+fj-pi*ti-pj*tj-pj*ti
如果先写j题Tj=fj-pj*tj+fi-pi*(ti+tj)=fi+fj-pi*ti-pj*tj-pi*tj
由Ti-Tj=pi*tj-pj*ti。所以。如果i题的性价比比j题的高,那么pi*tj>pj*ti
所以应当将性价比高的先写。所谓是同等时间内先拿分高的。
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <iostream>
using namespace std;

typedef long long ll;
const int mod = 1e9+7;
const double PI=3.14159265358;
const int INF=0x3f3f3f3f;

struct node{
    //分数
    ll fen;
    //每分钟减少的分数
    ll minfen;
    //花费的时间
    ll time;
    //自定义排序方法
    friend bool operator <(const node a,const node b){
        return b.time*a.minfen>a.time*b.minfen;
    }
};
node no[52];
//dp[i][j] i表示题数 j表示时间
ll dp[52][100010];
int t,n;



int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin>>n>>t;

    memset(dp,0,sizeof(dp));
    memset(no,0,sizeof(no));

    for(int i=1;i<=n;i++){
        cin>>no[i].fen;
    }
    for(int i=1;i<=n;i++){
        cin>>no[i].minfen;
    }
    for(int i=1;i<=n;i++){
        cin>>no[i].time;
    }
    //排序。将性价比高的排在前面
    sort(no+1,no+n+1);

    ll ans=0;
    //循环题数
    for(int i=1;i<=n;i++){
        //循环时间
        for(int j=0;j<=t;j++){
            //当前时间足够写完当前题目时
            if(j>=no[i].time){
                //等于当前时间内完成i-1题的分数与
                //(当前时间-这一题的时)间内完成i-1的分数加上写完这一题的分数
                //中的最大值
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-no[i].time]+no[i].fen-no[i].minfen*j);
            }else{
                //此时时间不够完成此题时
                //dp等于当前时间内完成i-1题的分数
                dp[i][j]=dp[i-1][j];
            }
            //答案等于这些方案中,分数最高的那个
            ans=max(ans,dp[i][j]);
        }
    }
    cout<<ans<<endl;

    return 0;
}
原文地址:https://www.cnblogs.com/mzchuan/p/13591526.html