1. 程式人生 > >BZOJ1270[BJWC2008]雷濤的小貓

BZOJ1270[BJWC2008]雷濤的小貓

雷濤同學非常的有愛心,在他的宿舍裡,養著一隻因為受傷被救助的小貓(當然,這樣的行為是違反學生宿舍管理條例的)。在他的照顧下,小貓很快恢復了健康,並且愈發的活潑可愛了。

可是有一天,雷濤下課回到寢室,卻發現小貓不見了!經過一番尋找,才發現她正趴在陽臺上對窗外的柿子樹發呆…

在北京大學的校園裡,有許多柿子樹,在雷濤所在的宿舍樓前,就有N棵。並且這N棵柿子樹每棵的高度都是H。冬天的寒冷漸漸籠罩了大地,樹上的葉子漸漸掉光了,只剩下一個個黃澄澄的柿子,看著非常喜人。而雷濤的小貓恰好非常的愛吃柿子,看著窗外樹上的柿子,她十分眼饞,於是決定利用自己敏捷的跳躍能力跳到樹上去吃柿子。

小貓可以從宿舍的陽臺上跳到窗外任意一棵柿子樹的樹頂。之後,她每次都可以在當前位置沿著當前所在的柿子樹向下跳1單位距離。當然,小貓的能力遠不止如此,她還可以在樹之間跳躍。每次她都可以從當前這棵樹跳到另外的任意一棵,在這個過程中,她的高度會下降Delta單位距離。每個時刻,只要她所在的位置有柿子,她就可以吃掉。整個“吃柿子行動”一直到小貓落到地面上為止。

雷濤調查了所有柿子樹上柿子的生長情況。他很想知道,小貓從陽臺出發,最多能吃到多少柿子?他知道寫一個程式可以很容易的解決這個問題,但是他現在懶於寫任何程式碼。於是,現在你的任務就是幫助雷濤寫一個這樣的程式。

圖為N=3,H=10,Delta=2的一個例子。小貓按照圖示路線進行跳躍,可以吃到最多的8個柿子
image

輸入格式

第一行三個整數N,H,Delta

接下來N行,每行一個整數Ni代表第i個樹上柱子的數量

接下來Ni個整數,每個整數Tij代表第i個樹的高度Tij上有一個柿子

1<=N,H<=2000

0<=Ni<=5000

1<=Delta<=N

1<=Ti<=H

輸入檔案不大於40960Kb

輸出格式

小貓能吃到多少柿子

樣例輸入1

3 10 2
3 1 4 10
6 3 5 9 7 8 9
5 4 5 3 6 9

樣例輸出1

8

題解

非常簡單的DP題,因為過程本身的可逆性,從高處往低處走和從低處往高處走的效果是一樣的。我們設DP[i][j]表示當高度為i時站在第j顆樹上最多可以吃到的果子數量,則如果從高處往低處走的轉移方程很容易可以想到是
\[ dp[i][j]=\begin{cases} dp[i][j]+dp[i+1][j]&i+detal>n\\ max(dp[i][j]+dp[i+1][j],dp[i][j]+max\_note[i+detal]) &i+detal<=n\\ \end{cases} \]


其中$ max_note $記錄每一層果子的最大數量。注意max_note要在DP的過程中持續更新,因為每一層的最大值是隨時都在變動的。

#include<bits/stdc++.h>
using namespace std;
inline char get(){
    static char buf[300],*p1=buf,*p2=buf;
    return p1==p2 && (p2=(p1=buf)+fread(buf,1,300,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register char c=get();register int f=1,_=0;
    while(c>'9' || c<'0')f=(c=='-')?-1:1,c=get();
    while(c<='9' && c>='0')_=(_<<3)+(_<<1)+(c^48),c=get();
    return _*f;
}
int dp[2005][2005];//第一維記高度,第二維記位置 
int tot[2005];//記錄總量 
int netmax[2005];//當前高度最大值 
int n,h,detal;
int main(){
    //freopen("1.txt","r",stdin);
    n=read();h=read();detal=read();
    for(register int i=0;i<2005;i++){
        for(register int j=0;j<2005;j++)dp[i][j]=0;
    }
    for(register int i=1;i<=n;i++){
        tot[i]=read();
        for(register int j=1;j<=tot[i];j++){
            int cas=read();
            dp[cas][i]++;
        }
    }
    for(register int i=h;i>=1;i--){//高度 
        for(register int j=n;j>=1;j--){//樹 
            if(i+detal<=h)dp[i][j]=max(dp[i][j]+dp[i+1][j],dp[i][j]+netmax[i+detal]);
            else dp[i][j]+=dp[i+1][j];
            netmax[i]=max(netmax[i],dp[i][j]);
        }
    }
    int out=-1;
    for(register int i=1;i<=n;i++)out=max(out,dp[1][i]);
    cout<<out;
    return 0;
}