版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/PK__PK/article/details/82971159
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1636
题意:
题解:根据题意,很容易写出DP【i】【j】表示第 i 天,作业量为 j 的最大的总作业量。但是 j 为1e16很大,空间爆炸。所以需要想办法,把每天的作业量表示出来。根据数据可知,每天的作业量的最大值最小值不超过100,根据这个条件。我们显然能推出
DP【i】【j】【L】表示第 i 天选 第 j 门,且差值为L的最大的总作业量。那么我们就可以用 j 和 L表示某天选择 j 门课所获等的作业量为 a【j】 + L 。
那么就可以转移了。
首先 第 i 天 一定是 又 i - 1 天转移回来,只有两种转移方式。最大总作业量 减 k 或 除 k。还要保证 复杂度是严格递增的,所以还需要先按照复杂度 排序。
所以我们需要依次枚举天数 i , 和第 i 天的选择哪门课,还有这门课的差值L,还有 i - 1天的课程。
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long dp[55][55][105];
struct node{
long long a,b,nan,cha; // 最小值,最大值,难度,差值
}Class[55];
bool cmp(node a,node b){ // 按照难度排序。
return a.nan < b.nan;
}
int main(){
long long n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i = 1 ; i <= m ; i ++){
scanf("%lld%lld%lld",&Class[i].a,&Class[i].b,&Class[i].nan);
Class[i].cha = Class[i].b - Class[i].a;
}
sort(Class+1,Class+1+m,cmp);
memset(dp,0,sizeof(dp)); //初始化为 0
for(int i = 1 ; i <= m ; i ++){ // 初始化第一天
for(int L = 0 ; L <= Class[i].cha ;L++)
dp[1][i][L] = Class[i].a + L;
}
for(int i = 2 ; i <= n ; i ++){ // 枚举天数
for(int j = 1 ; j <= m ; j ++){ // 枚举 哪门课
for(int L = 0 ; L <= Class[j].cha ; L ++){
long long now = Class[j].a + L; // i天选择j门课获得作业量
long long pre1 = now - k;
long long pre2 = now / k;
for(int x = 1 ; x < j ; x ++){ // 枚举i-1天的难度
if(Class[x].nan < Class[j].nan){ // 保证难度严格单调。
if(pre1 >= Class[x].a && pre1 <= Class[x].b && dp[i-1][x][pre1 - Class[x].a] != 0){
dp[i][j][L] = max(dp[i][j][L],dp[i-1][x][pre1 - Class[x].a] + now);
}
if(pre2 >= Class[x].a && pre2 <= Class[x].b && now%k == 0 &&dp[i-1][x][pre2 - Class[x].a] != 0){
dp[i][j][L] = max(dp[i][j][L],dp[i-1][x][pre2 - Class[x].a] + now);
}
}
}
}
}
}
long long ans = 0;
for(int i = 1 ; i <= m ; i ++){
for(int j = 0 ; j <= Class[i].cha ; j ++){
ans = max(dp[n][i][j],ans);
// cout << ans << endl;
}
}
if(ans){
printf("YES\n");
cout << ans << endl;
}
else {
printf("NO\n");
}
}