hdu 4512 吉哥系列故事——完美隊形I【LCIS經典應用】

連接:



吉哥系列故事——完美隊形I

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1454    Accepted Submission(s): 420


Problem Description
  吉哥這幾天對隊形比較感興趣。
  有一天,有n我的按順序站在他的面前,他們的身高分別是h[1], h[2] ... h[n],吉哥但願從中挑出一些人,讓這些人造成一個新的隊形,新的隊形若知足如下三點要求,則稱之爲完美隊形:
  
  一、挑出的人保持他們在原隊形的相對順序不變;
  二、左右對稱,假設有m我的造成新的隊形,則第1我的和第m我的身高相同,第2我的和第m-1我的身高相同,依此類推,固然,若是m是奇數,中間那我的能夠任意;
  三、從左到中間那我的,身高需保證遞增,若是用H表示新隊形的高度,則H[1] < H[2] < H[3] .... < H[mid]。

  如今吉哥想知道:最多能選出多少人組成完美隊形?
 

Input
  第一行輸入T,表示總共有T組數據(T <= 20);
  每組數據先輸入原先隊形的人數n(1<=n <= 200),接下來一行輸入n個整數,表示按順序從左到右原先隊形位置站的人的身高(50 <= h <= 250,不排除特別矮小和高大的)。
 

Output
  請輸出能組成完美隊形的最多人數,每組數據輸出佔一行。
 

Sample Input
   
   
   
   
2 3 51 52 51 4 51 52 52 51
 

Sample Output
   
   
   
   
3 4
 

Source
 

Recommend
liuyiding



算法:LCIS 【最長公共上升子序列分析



正解思路:


主要路線:先輸入數組 a[] , 而後另外用一個數組 b[] 反序存 a[], 而後求 a 和 b 的 LCIS

關鍵:如何解決 中間的人 在哪兒?如何避免 過多 重疊?


PS:因爲是從 1 開始記錄的,因此序列 a[i] 對應於序列 b[n-i+1]

用 dp[i][j] 表示 a 從 1到 i , b[] 從 1 到 j ,兩段序列的 LCIS

解決方法:每次枚舉只重疊一我的。。。重疊的人的編號 i 從 1 到 n-1【這樣就必定保證了通過後面的處理,不會出現過多的重疊】
                  那麼每次求 LCIS 時 a[] 對應從 1 到 i
                                                  b[] 對應從 1 到 n-i+1
                  那麼每次更新 ans 最多隻可能重疊了一人。【也就是題目中說的最終完美隊列中有奇數我的】
                  而後再根據所求的最大 dp[i][j] 的編號 i 和 j 判斷是否重疊了
                  若是對應於當前最大的 dp[i][j] :i < n-j+1 說明當前最高的人沒有重疊, ans = max(ans, dp[i][j]*2)
                                                                   不然最高的人重疊了, ans = max(ans, dp[i][j]*2-1)

PS:下面貼上個人心酸 WA 史和思路歷程哭


錯誤思想分析:

誤區一:寫着寫着,發現沒有測試出錯誤大哭

昨晚比賽的時候,先是想着定義 ans 爲全局變量,而後用 LCIS 返回在 b[] 中找到的最高的人的編號 index
而後再對應於它在 a[] 中的編號 index1 = n-index+1 ,看 a 中 index1 前面有沒有一個這樣高的人
若是有,那麼說明最高的人沒有重疊 ans = ans*2
若是 a[] 中 1 到 index1-1 中沒有一個這樣高的人那麼 ans = ans*2-1.....不知道哪裏錯了,求數據求指導,附上WA了的代碼




誤區二:

今天早上看了下別人發的博客都是邊求 LCIS 邊更新 ans 的。
因而乎,就沒有管重疊了多少人直接 LCIS(n,n)
void LCIS(int n)
{
    ans = 1;
    for(int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for(int j = 1; j <= n; j++) //想來有了下面的下標處理是不會出現過多重疊的,錯的很嚴重。。。
        {
            if(a[i] > b[j])
                tmp = max(tmp,dp[j]);
            else if(a[i] == b[j])
                dp[j] = max(dp[j],tmp+1);
            if(i < (n-j+1)) //判斷是否重疊
                ans = max(ans, dp[j]*2);
            else ans = max(ans, dp[j]*2-1);
        }
    }
}

這樣的代碼對於下面的數據樣例:

1
5
1 3 2 1 3

正確的完美序列該是 1 3 1 或者 1 2 1 均可以,那麼應該輸出的是 3
可是求出最後的的 dp[] 的最大值確是3
序列: 1 3 2 1 3 
            3 1 23 1
而後到了下面判斷重疊的狀況的時候直接  dp[]*2-1 = 5 確定是比原來的正確的 ans 要大的,而後就錯了。。。

改正 只要把第二重循環 j < n 改爲 j < n-i+1 便可以過,保證最多隻重疊一人

第一種思路開始想着錯誤的應該和這個差很少,可是實在是沒有 找出錯誤樣例,但願路過的大神指點下=_=!


LCIS模板

PS: 不熟悉的,本身能夠先作下 hdu 1424 看下:

const int maxn = 500+50;
int dp[maxn];
int a[maxn],b[maxn];

/*********************************************
求序列 a (長度爲 n)和序列 b (長度爲 m)的 LCIS
下標從 1 開始
返回 LCIS 長度
*********************************************/
int LCIS(int n,int m)
{
    for(int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for(int j = 1; j <= m; j++)
        {
            if(a[i] > b[j])
            {
                tmp = max(tmp,dp[j]);
            }
            else if(a[i] == b[j])
                dp[j] = max(dp[j],tmp+1);
        }
    }

    int ans = 0;
    for(int i = 1; i <= m; i++)
        ans = max(ans, dp[i]);
    return ans;
}



code:

code1: 用一次 LCIS ,邊求LCIS 邊處理重疊的人,更新 ans
2013-08-04 13:58:02 Accepted 4512 15MS 232K 988 B C++ free斬

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 210;
int a[maxn], b[maxn];
int dp[maxn];
int n;
int ans;

void LCIS(int n,int m)
{
    ans = 1;
    for(int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for(int j = 1; j <= (n-i+1); j++) //注意:最多中間只重複一人,方便後面的重疊處理
        {
            if(a[i] > b[j])
                tmp = max(tmp,dp[j]);
            else if(a[i] == b[j])
                dp[j] = max(dp[j],tmp+1);
            if(i < (n-j+1)) //判斷是否重疊
                ans = max(ans, dp[j]*2);
            else ans = max(ans, dp[j]*2-1);
        }
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            b[n-i+1] = a[i];
        }
        memset(dp,0,sizeof(dp));
        LCIS(n, n);
        printf("%d\n", ans);
    }
    return 0;
}


code2: 枚舉重疊的人進行 LCIS 2*N 次,思路同樣效率稍微低一點,不過比較好理解
2013-08-04 10:40:36 Accepted 4512 125MS 232K 1064 B C++ free斬

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn = 210;
int a[maxn], b[maxn];
int dp[maxn];
int n;

int LCIS(int n,int m)
{
    memset(dp,0,sizeof(dp));
    for(int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for(int j = 1; j <= m; j++)
        {
            if(a[i] > b[j])
                tmp = max(tmp,dp[j]);
            else if(a[i] == b[j])
                dp[j] = max(dp[j],tmp+1);
        }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++)
        ans = max(ans, dp[i]);
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            b[n-i+1] = a[i];
        }
        int ans = 1;
        for(int i = 1; i < n; i++)
        {
            ans = max(ans, 2*LCIS(i, n-i)); //不重疊
            ans = max(ans, 2*LCIS(i+1, n-i)-1); //最壞剛好重疊一人
        }
        printf("%d\n", ans);
    }
    return 0;
}
相關文章
相關標籤/搜索