HDU 6049 Sdjpx Is Happy(dp)

xiaoxiao2021-02-28  109

Description 给出一个1~n的排列,要其将其分成k段,每段内部变成升序,且可以交换两段的值,问使得该排列变成1~n的k的最大值 Input 第一行一整数T表示用例组数,每组用例首先输入一整数n表示序列长度,之后一个1~n的排列a[i]~a[n] (1<=n<=3000) Output 对于每组用例,输出使得a[1]~a[n]可以变成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],维护一个最大值即为答案,由于条件限制,可以交换的段不超过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][n]); 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][n]==k+1&&mx[k+1][n]==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][n]+2); } } printf("%d\n",ans); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-50582.html

最新回复(0)