题目描述
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);