51NOD 1636 教育改革 (DP+思維)
阿新 • • 發佈:2018-12-14
題意:
題解:根據題意,很容易寫出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"); } }