1. 程式人生 > >[USACO10JAN]乳酪塔Cheese Towers

[USACO10JAN]乳酪塔Cheese Towers

題目描述

FJ要建一個乳酪塔,高度最大為T。他有N種乳酪(每種乳酪無限個)。第i種乳酪的高度為Hi(一定是5的倍數),價值為Vi。一塊高度Hi>=K的乳酪被稱為大乳酪,一個乳酪如果在它上方有大乳酪(如果有多塊就只算一次),它的高度Hi就會變成原來的4/5.。FJ想讓他的乳酪塔價值和最大。請你求出這個最大值。

輸入格式:

第一行三個數N,T,K,意義如上所述。 接下來n行,每行兩個數V_i,h_i(注意順序)

輸出格式:

乳酪塔的最大價值

(1 <= T <= 1,000).

N (1 <= N <= 100) 

(1 <= V_i <= 1,000,000)

H_i (5 <= H_i <= T)

這一題不難看出是揹包問題,因為dp在這一道題無後效性!

而且N和T的值很小,揹包的時間是O(NT),完全承受的了的

可是如果直接從底往上搜,就會很難判斷出上面是否有大乳酪,那我們怎麼辦呢?

既然直接模擬不行,那我們換個方向總行吧!

所以我們直接從上往下找

//dp[i][0]表示第i高度,沒有大乳酪的最大值
//dp[i][0]表示第i高度,有大乳酪的最大值

就可以推出動態方程了

首先是標頭檔案和定義的陣列:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<cmath>
#include<string>
#include<set>
#include<ctime>
using namespace std;
inline int read(){
	int x=0,f=0;char s=getchar();
	while(!isdigit(s))f|=s=='-',s=getchar();
	while( isdigit(s))x=(x<<1)+(x<<3)+s-48,s=getchar();
	return !f?x:-x;
}
inline void print(int x){
     if(x<0)putchar('-'),x=-x;
     if(x>9)print(x/10);
     putchar(x%10+'0');
}
const int N=2e2+20;
const int T=2e3+20;
int n,t,k;
struct node{
	int v,h;
}a[N];
int dp[T][2];
//dp[i][0]表示第i高度,沒有大乳酪的最大值
//dp[i][0]表示第i高度,有大乳酪的最大值

然後是輸入和初始化

dp一開始全部為0,表示不可用,邊界dp[0][0]=0

n=read();t=read();k=read();
	for(int i=1;i<=n;i++)a[i].v=read(),a[i].h=read();
	memset(dp,-1,sizeof(dp));dp[0][0]=0;

然後是核心的dp

我們可以分3種不同的情況進行討論

1:

當前已經有大乳酪了,就推出dp[ki+a[i].h/5*4][1]

2:

當前無大乳酪,推出dp[ki+a[i].h][0]

3:

當前無大乳酪並且這個就是大乳酪,利用dp[ki][0]推出dp[ki+a[i].h][1]

程式碼:

for(int ki=0;ki<=t;ki++){
	for(int i=1;i<=n;i++){
		if(ki+a[i].h/5*4<=t&&dp[ki][1]!=-1)
			dp[ki+a[i].h/5*4][1]=max(dp[ki+a[i].h/5*4][1],dp[ki][1]+a[i].v);
		if(ki+a[i].h    <=t&&dp[ki][0]!=-1)
			dp[ki+a[i].h    ][0]=max(dp[ki+a[i].h    ][0],dp[ki][0]+a[i].v);
		if(a[i].h>=k	   &&dp[ki][0]!=-1)
			dp[ki+a[i].h    ][1]=max(dp[ki+a[i].h    ][1],dp[ki][0]+a[i].v);
	}
}

以及最後的輸出

int maxx=-1;
for(int i=0;i<=t;i++)maxx=max(maxx,max(dp[i][0],dp[i][1]));
print(maxx);