1. 程式人生 > >51NOD 1636 教育改革 (DP+思維)

51NOD 1636 教育改革 (DP+思維)

題意:

題解:根據題意,很容易寫出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");
	}

}