原题链接:1270: [BeijingWc2008]雷涛的小猫
看到这个题,首先想到的就是动态规划(直觉)。
一、O(n^3)算法
而且这道题的状态转移方程也很好想:dp[i][j]表示高度为i,在j棵树上吃到的最大柿子数,num[i][j]表示在i棵树上j高度上的柿子数。
dp[i][j]=max{dp[i][j],dp[i+1][j]+num[i][j],dp[i+del][k]+num[i][j]};
下面是丑陋的超时代码。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,h,del,tmp,t;
int tree[2009][2009],dp[2009][2009];
void read(int &a){
a=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
return;
}
int main()
{
read(n);read(h);read(del);
for(int i=1;i<=n;i++){
read(tmp);
for(int j=1;j<=tmp;j++){
read(t);
tree[i][t]++;
}
}
for(int i=h;i>0;i--){
for(int j=1;j<=n;j++){
if(i==h){
dp[i][j]=tree[j][i];
continue;
}
for(int k=1;k<=n;k++){
tmp=dp[i+1][j]+tree[j][i];
if(i+del<=h)t=dp[i+del][k]+tree[j][i];
else t=0;
dp[i][j]=max(dp[i][j],max(tmp,t));
}
}
}
t=0;
for(int i=1;i<=n;i++){
t=max(t,dp[1][i]);
}
cout<<t<<endl;
return 0;
}
二、O(n^2)算法
注意到不需要枚举上一步是从哪棵树转移过来的,只需要知道上面那一层的最大柿子数即可。
用lev[h]保存h高度的最大柿子数,now[i]表示跳到现在这棵树能吃到的最大柿子数。
now[j]=max{lev[i-del]+num[j][i]};
lev[i]=max{now[j]1<=j<=n};
最后输出lev[1]即为最后答案。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,h,del,tmp,t;
int tree[2009][2009],lev[5001],now[5001];
void read(int &a){
a=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
return;
}
int main()
{
read(n);read(h);read(del);
for(int i=1;i<=n;i++){
read(tmp);
for(int j=1;j<=tmp;j++){
read(t);
tree[i][t]++;
}
}
for(int i=h;i>0;i--){
t=(i+del<=h)?lev[i+del]:0;
for(int j=1;j<=n;j++){
now[j]=max(now[j],t)+tree[j][i];
lev[i]=max(lev[i],now[j]);
}
}
printf("%d",lev[1]);
return 0;
}