連接:
吉哥系列故事——完美隊形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
Source
Recommend
liuyiding
正解思路:
主要路線:先輸入數組 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模板:
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
#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 次,思路同樣效率稍微低一點,不過比較好理解
#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;
}