HDU 4513 吉哥系列故事——完美队形II
题意:
- n个人,他们的身高分别是h[1], h[2] … h[n],从中挑出一些人,让这些人形成一个新的队形,新的队形若满足以下三点要求,则就是新的完美队形:
1、挑出的人保持原队形的相对顺序不变,且必须都是在原队形中连续的;
2、左右对称,假设有m个人形成新的队形,则第1个人和第m个人身高相同,第2个人和第m-1个人身高相同,依此类推,当然如果m是奇数,中间那个人可以任意;
3、从左到中间那个人,身高需保证不下降,如果用H表示新队形的高度,则H[1] <= H[2] <= H[3] … <= H[mid]。
- 求完美队列的最大人数
思路:
- 因为身高是数字,所以我们处理数组的时候两两之间插入0,然后第一位插入-1
- 然后我们在更新radius[ ]的时候,由于左半边身高不下降排列、右半边不上升排列,所以我们需要判断当前关于 i 左右对称的这两个点是不是符合要求。分两种情况来考虑:1、如果是0,就直接半径+1 . 2、如果非0,那它里面的那一个就一定是0,那么就需要与0前面的那个数比较,看是不是满足条件。如果两种条件都不满足,那么我们的“完美队列”就此中断
- 剩下的就都是Manacher的模板了
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxN = 1e5 + 5;
const int maxM = 2e5 + 7;
int n;
int Manacher(int * a)
{
int len = (n << 1) + 2;
vector<int>New(len, 0);
New[0] = -1;
New[1] = 0;
int cnt = 1;
for(int i = 0; i < n; i ++ )
{
New[++ cnt ] = a[i];
New[++ cnt ] = 0;
}
vector<int>radius(len, 0);
int mx = 0, id = 0, Center = 0, Len = 0;
for(int i = 1; i < len; i ++ )
{
radius[i] = mx > i ? min(mx - i , radius[(id << 1) - i]) : 1;
while(i + radius[i] < len && i - radius[i] >= 0 && New[i + radius[i]] == New[i - radius[i]])
{
if(New[i + radius[i]] == 0)//如果当前位是0,那么就直接半径+1
++ radius[i];
else if(New[i + radius[i]] <= New[i + radius[i] - 2])//前一位是0,那么需要与0的前一位比较
++ radius[i];
else//一旦不满足规则,就直接break
break;
}
if(mx < radius[i] + i)
{
mx = radius[i] + i;
id = i;
}
if(Len < radius[i])
{
Len = radius[i];
Center = i;
}
}
return Len - 1;
}
int main()
{
int T; scanf("%d", &T);
while(T -- )
{
scanf("%d", &n);
int a[maxN];
for(int i = 0; i < n; i ++)
scanf("%d", &a[i]);
printf("%d\n", Manacher( a ));
}
return 0;
}