COCI 蚱蜢

蚱蜢

Description

一隻蚱蜢來到一個花田裏,花田有N*N格,每格中有一朵花,已知每朵花的花瓣數量。這隻蚱蜢,最開始在第R行第C列,它打算遍歷儘可能多的。它按照下列規則進行遍歷:
(1) 跳到相鄰的行,並且列座標之差要大於1,即 |r1 -r2| = 1 and |c1 -c2|> 1
(2) 跳到相鄰的列,並且行座標之差要大於1,即 |c1 -c2| = 1 and |r1 -r2|> 1
(3) 下一朵花的花瓣數量要大於當前這朵。
請你幫它算算,最多遍歷多少朵花。

Input Description

第一行一個整數N,表示花田的大小。
第二行兩個整數R和C,表示蚱蜢剛開始的位置。
接下來N行,每行N個數字,表示每朵花的花瓣數量。

Output Description

輸出一行,該行包含一個整數:蚱蜢最多能遍歷的花朵數量。

Sample Input

4
1 1
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7

5
3 3
20 16 25 17 12
11 13 13 30 17
15 29 10 26 11
27 19 14 24 22
23 21 28 18 13

Sample Output

4

21

Data Size & Hint

對於50%的數據,N不超過100
對於80%的數據,N不超過1000
對於100%的數據,N不超過1500,花瓣爲不超過106 的正數

很顯然,這道題滿足拓撲序,是一道dp題
然而是個人都知道在矩陣dp。。有點虛啊
這個dp有兩維
一維是下標,另一位是花瓣數量
這道題顯然很難使下標滿足拓撲序
於是想要降維就只能用花瓣數量排序
這個時候我們就不用考慮花瓣數量的影響了
其實還是需要考慮的,花瓣數量相等時不能互相傳遞
然而這個dp的複雜度是O(n3 )明顯超時Orz。。。
於是讓我們想想怎麼優化
一個比較神的想法就是
花瓣轉移不是要從兩個下標分別差1k (k>1 )的花上轉移過來嗎?
就算相鄰的行(或列)最大的3個dp值分別在這3個位置上,那第四個位置呢?
如果我們對於每一行和每一列,儲存他們前四大的dp值,那麼所有的情況都會被考慮到
但是有一個問題是,
但dp轉移的時候我們仍舊是需要判斷花瓣數是否相等的
如果這四個點的花瓣數都與這個待轉移點相等的話,不就Orz了麼。。(雖然並沒有這種噁心數據)
這個時候我們就可以延遲更新,但花瓣數爲x的點全部轉移完畢後再一起更新
於是一個完美的O(n2 )代碼就出爐了(好吧,係數賊大。。。。)

#include<bits/stdc++.h>
using namespace std;
#define M 1555
#define N 1000005
void Rd(int &res){
//scanf和cin讀入的複雜度大約是log級別的,cin係數稍大,1000000的數據不用讀入掛,怎麼死的都不知道
    char c;res=0;
    while(c=getchar(),!isdigit(c));
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),isdigit(c));
}
int dp[M*M],mx[6][M],my[6][M];
struct node{
    int x,y,val;
}q[M*M],s[M*M];
int cnt[N];
int main(){
    int n,x,y,k=0,ans=1,l;
    scanf("%d",&n);
    scanf("%d %d",&x,&y);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            Rd(q[++k].val);//將矩陣轉化爲線性結構
            q[k].x=i;q[k].y=j;
        }
    for(int i=1;i<=k;i++)cnt[q[i].val]++;
    for(int i=1;i<=1000000;i++)cnt[i]+=cnt[i-1];
    for(int i=k;i>=1;i--)s[cnt[q[i].val]--]=q[i];
    for(int i=1;i<=k;i++)if(s[i].x==x&&s[i].y==y){l=i;break;}
    //計數排序的速度比sort還快好多
    dp[l]=1;
    int pre=l;
    for(int i=l+1;i<=k;i++){
        if(s[i].val!=s[pre].val){//延遲更新
            for(int t=pre;t<i;t++){
                for(int j=1;j<=4;j++)
                    if(dp[t]>dp[mx[j][s[t].x]]){
                        for(int p=4;p>j;p--)mx[p][s[t].x]=mx[p-1][s[t].x];
                        mx[j][s[t].x]=t;
                        break;
                    }
                for(int j=1;j<=4;j++)
                    if(dp[t]>dp[my[j][s[t].y]]){
                        for(int p=4;p>j;p--)my[p][s[t].y]=my[p-1][s[t].y];
                        my[j][s[t].y]=t;
                        break;
                    }
            }
            pre=i;
        }
        for(int j=1;j<=4;j++)//dp轉移
            if(s[i].x>1){
                int id=mx[j][s[i].x-1];
                if(id&&abs(s[i].y-s[id].y)>1&&dp[i]<dp[id]+1){
                    dp[i]=dp[id]+1;
                    break;
                }
            }
        for(int j=1;j<=4;j++)//都是複製粘貼的Orz。。
            if(s[i].x<n){
                int id=mx[j][s[i].x+1];
                if(id&&abs(s[i].y-s[id].y)>1&&dp[i]<dp[id]+1){
                    dp[i]=dp[id]+1;
                    break;
                }
            }
        for(int j=1;j<=4;j++)
            if(s[i].y>1){
                int id=my[j][s[i].y-1];
                if(id&&abs(s[i].x-s[id].x)>1&&dp[i]<dp[id]+1){
                    dp[i]=dp[id]+1;
                    break;
                }
            }
        for(int j=1;j<=4;j++)
            if(s[i].y<n){
                int id=my[j][s[i].y+1];
                if(id&&abs(s[i].x-s[id].x)>1&&dp[i]<dp[id]+1){
                    dp[i]=dp[id]+1;
                    break;
                }
            }
        if(dp[i]>ans)ans=dp[i];//更新答案
    }
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章