您的位置:首页 > 移动开发

HDU 6049 Sdjpx Is Happy(dp)

2017-08-06 15:01 281 查看
Description

给出一个1~n的排列,要其将其分成k段,每段内部变成升序,且可以交换两段的值,问使得该排列变成1~n的k的最大值

Input

第一行一整数T表示用例组数,每组用例首先输入一整数n表示序列长度,之后一个1~n的排列a[i]~a
(1<=n<=3000)

Output

对于每组用例,输出使得a[1]~a
可以变成1~n的k的最大值

Sample Input

2

5

1 5 4 3 2

5

4 5 1 2 3

Sample Output

4

2

Solution

num[i][j]表示区间[i,j]在不进行段之间的交换的情况下可以分成的最多段数,一个区间[i,j]可以被分成一段当且仅当mx[i][j]-mn[i][j]=j-i,其中mx[i][j]和mn[i][j]表示该区间的最大值和最小值,O(n^2)预处理出mx和mn,对于num[i][j],如果num[i][j]想被分成多段,那么mn[i][j]必须在第一段内,且j-i=mx[i][i]-mn[i][j],num也可以在O(n^2)预处理,之后枚举要交换的第一段,假设为[i,j],首先[i,j]是连续的,而且要么i是1,要么[1,i-1]排完序后是1~i-1,令k=mx[i][j],那么要交换的第二段的右端点必然是k,且要么k是n,要么[k+1,n]排完序后是k+1~n,这些条件均满足后枚举要交换的第二段的左端点l,如果[l,k]连续且mn[l][k]=i则说明这两个区间可交换,分成的段数为num[1][i-1]+1+num[j+1][l-1]+1+num[k+1]
,维护一个最大值即为答案,由于条件限制,可以交换的段不超过n^2个

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int maxn=3005;
int T,n,a[maxn],num[maxn][maxn],mx[maxn][maxn],mn[maxn][maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
mx[i][i]=mn[i][i]=a[i];
for(int j=i+1;j<=n;j++)
mx[i][j]=max(mx[i][j-1],a[j]),mn[i][j]=min(mn[i][j-1],a[j]);
}
for(int i=1;i<=n;i++)
{
int res=1;
num[i][i]=1;
for(int j=i+1;j<=n;j++)
{
if(mn[i][j]!=mn[i][j-1])res=0;
if(mx[i][j]-mn[i][j]==j-i)num[i][j]=++res;
}
}
int ans=max(1,num[1]
);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
if(num[i][j]&&(i==1||mn[1][i-1]==1&&mx[1][i-1]==i-1))
{
int k=mx[i][j];
if(k==n||mn[k+1]
==k+1&&mx[k+1]
==n)
{
for(int l=k;l>j;l--)
if(num[l][k]&&mn[l][k]==i)
ans=max(ans,num[1][i-1]+num[j+1][l-1]+num[k+1]
+2);
}
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: