神祕的咒語 LCIS


https://vijos.org/p/1264

看到題目是lcs+lis的題目.


身爲拜月教的高級間諜,你的任務總是逼迫你出生入死。比如這一次,拜月教主就派你跟蹤趙靈兒一行,潛入試煉窟底。

據說試煉窟底藏着五行法術的最高法術:風神,雷神,雪妖,火神,山神的咒語。爲了習得這些法術,要付出艱辛的努力,但是回報同樣十分豐厚。

拜月希望你告訴他咒語的長度爲多少。(你:“請問您想知道咒語的具體內容嗎?”拜月:“想,但是vijos不支持special judge。”-_-原來大人物也有大人物的悲哀。。。)
於是你偷偷躲在一邊,想乘機看看咒語究竟是什麼。突然,天空(??試煉窟底看的到天空??)出現了兩條非常長的數字串,你抓狂了。究竟哪個纔是真正的咒語呢?你突然想到,這兩個都不是咒語(不妨稱之爲僞咒語),而真正的咒語卻與他們有着密切的聯繫。於是你打開拜月親手給你寫的紙條:"The Real Incantation is Their Common Increasing Subsequence of Maximal Possible Length"
"該死的拜月,居然還會E文!"你咒罵着,但爲了一家老小的生命,又不得不賣命地算着咒語的長度。

格式

輸入格式

第一行爲1個數N,代表有N組測試數據。

對於每組測試數據,描述了兩條數字串,首先一個數字爲一條僞咒語的長度M,接下來M個數描述了僞咒語的內容。

輸出格式

共N行,每行一個數字,描敘了對應咒語的長度。

樣例1

樣例輸入1[複製]

1
5 1 4 2 5 -12
4 -12 1 2 4

樣例輸出1[複製]

2

想狀態方程

首先我們可以看到,這個問題具有相當多的重疊子問題。於是我們想到用DP搞。DP的首要任務是什麼?定義狀態。

1定義狀態F[i][j]表示以a串的前i個字符b串的前j個字符且以b[j]爲結尾構成的LCIS的長度。

爲什麼是這個而不是其他的狀態定義?最重要的原因是我只會這個,還有一個原因是我知道這個定義能搞到平方的算法。而我這隻會這個的原因是,這個狀態定義實在是太好用了。這一點我後面再說。

我們來考察一下這個這個狀態。思考這個狀態能轉移到哪些狀態似乎有些棘手,如果把思路逆轉一下,考察這個狀態的最優值依賴於哪些狀態,就容易許多了。這個狀態依賴於哪些狀態呢?

首先,在a[i]!=b[j]的時候有F[i][j]=F[i-1][j]。爲什麼呢?因爲F[i][j]是以b[j]爲結尾LCIS,如果F[i][j]>0那麼就說明a[1]..a[i]中必然有一個字符a[k]等於b[j](如果F[i][j]等於0呢?那賦值與否都沒有什麼影響了)。因爲a[k]!=a[i],那麼a[i]F[i][j]沒有貢獻,於是我們不考慮它照樣能得出F[i][j]的最優值。所以在a[i]!=b[j]的情況下必然有F[i][j]=F[i-1][j]。這一點參考LCS的處理方法。

那如果a[i]==b[j]呢?首先,這個等於起碼保證了長度爲1LCIS。然後我們還需要去找一個最長的且能讓b[j]接在其末尾的LCIS。之前最長的LCIS在哪呢?首先我們要去找的F數組的第一維必然是i-1。因爲i已經拿去和b[j]配對去了,不能用了。並且也不能是i-2,因爲i-1必然比i-2更優。第二維呢?那就需要枚舉b[1]..b[j-1]了,因爲你不知道這裏面哪個最長且哪個小於b[j]。這裏還有一個問題,可不可能不配對呢?也就是在a[i]==b[j]的情況下,需不需要考慮F[i][j]=F[i-1][j]的決策呢?答案是不需要。因爲如果b[j]不和a[i]配對,那就是和之前的a[1]..a[j-1]配對(假設F[i-1][j]>0,等於0不考慮),這樣必然沒有和a[i]配對優越。(爲什麼必然呢?因爲b[j]a[i]配對之後的轉移是max(F[i-1][k])+1,而和之前的i`配對則是max(F[i`-1][k])+1。顯然有F[i][j]>F[i`][j],i`>i

於是我們得出了狀態轉移方程:

a[i]!=b[j]:   F[i][j]=F[i-1][j]

a[i]==b[j]:   F[i][j]=max(F[i-1][k])+1 1<=k<=j-1&&b[j]>b[k]

不難看到,這是一個時間複雜度爲O(n^3)DP,離平方還有一段距離。

但是,這個算法最關鍵的是,如果按照一個合理的遞推順序,max(F[i-1][k])的值我們可以在之前訪問F[i][k]的時候通過維護更新一個max變量得到。怎麼得到呢?首先遞推的順序必須是狀態的第一維在外層循環,第二維在內層循環。也就是算好了F[1][len(b)]再去算F[2][1]

如果按照這個遞推順序我們可以在每次外層循環的開始加上令一個max變量爲0,然後開始內層循環。當a[i]>b[j]的時候令max=F[i-1][j]。如果循環到了a[i]==b[j]的時候,則令F[i][j]=max+1

最後答案是F[len(a)][1]..F[len(a)][len(b)]的最大值。



</pre><pre name="code" class="cpp">
#include<stdio.h>
#include<string>
#include<cstring>
int f[1005][1005],a[1005],b[1005],i,j,t,n1,n2,max;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n1);
        for(i=1;i<=n1;i++) scanf("%d",&a[i]);
		 scanf("%d",&n2);
        for(i=1;i<=n2;i++) scanf("%d",&b[i]);
        memset(f,0,sizeof(f));
        for(i=1;i<=n1;i++)
        {
            max=0;
            for(j=1;j<=n2;j++)
            {
                f[i][j]=f[i-1][j];
                if (a[i]>b[j]&&max<f[i-1][j]) max=f[i-1][j];
                if (a[i]==b[j]) f[i][j]=max+1;
            }
        }
        max=0;
        for(i=1;i<=n2;i++) 
		{
			//printf("%d ",f[n1][i]);
			if (max<f[n1][i]) max=f[n1][i];
		}
        printf("%d\n",max);
    }
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章