题目链接:D.Drop Voicing
这个题多亏队友的思路才让我醍醐灌顶,有解可寻。
题意
给你n个数的一种排列,对这个数列有两种操作。
- [第一种] Drop-2: 把 第 二 高 位 移 到 最 低 位 , 如 p n − 1 , p 1 , p 2 , . . . . . , p n − 2 , p n {把第二高位移到最低位,如p_{n-1},p_1,p_2,.....,p_{n-2},p_{n}} 把第二高位移到最低位,如pn−1,p1,p2,.....,pn−2,pn
- [第二种] Invert: 把 最 低 位 移 到 最 高 位 , 如 p 2 , p 3 , . . . . . , p n − 1 , p n , p 1 {把最低位移到最高位,如p_2,p_3,.....,p_{n-1},p_{n},p_1} 把最低位移到最高位,如p2,p3,.....,pn−1,pn,p1
任意连续的的Drop-2操作被称为一个muti-drop,问如何操作使得排列转变为1,2,…,n,并且要求muti-drop的数量最少。
题解
我们来看这两个操作,第一种:可以看出与
p
n
{p_n}
pn无关,只是前n-1个数字的循环排列,第二种是这n个数字的循环排列。
循环排列:
原
序
列
:
p
1
,
p
2
,
.
.
.
.
,
p
n
,
循
环
排
列
后
:
p
i
,
p
i
+
1
,
.
.
.
,
p
n
,
p
1
,
p
2
,
.
.
,
p
i
−
1
{原序列:p_1,p_2,....,p_n,循环排列后:p_i,p_{i+1},...,p_n,p_1,p_2,..,p_{i-1}}
原序列:p1,p2,....,pn,循环排列后:pi,pi+1,...,pn,p1,p2,..,pi−1
此时队友给了我一种思路,这两种操作可以合并为一个操作,就是将最后的一个数字 p n {p_n} pn插入到前(n-1)个数的任意位置。将插入的位置通过第一种操作移动到(n-1)个数里的最高端,然后通过第二种操作将第一个数移动到最高端就达到了插入操作。
这样我们可以通过计算最长上升子序列个数,然后用(长度-最长上升子序列个数)= 要插入的最少数字个数 = min(muti-drop)
由于原数列可以通过第二种操作进行循环排列,所以我们应枚举出每一种循环排列的情况,然后分别求出最长上升子序列的值,取其中的最大值,来保证(长度-最长上升子序列个数)min,来达到 min(muti-drop)。
代码
int p[maxn];
int dp[maxn],g[maxn],n;
int lis(vector<int> a)
{
for(int i=1;i<=n;i++) g[i]=inf;
int maxx=-1;
for(int i=0;i<a.size();i++)
{
int k=lower_bound(g+1, g+n+1, a[i])-g;
dp[i]=k;
g[k]=a[i];
maxx=max(maxx,dp[i]);
}
return maxx;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> p[i];
int maxx=-1;
for(int i=0;i<n;i++)
{
vector<int> a;
for(int j=i;j<i+n;j++) a.push_back(p[j%n]);
maxx=max(maxx,lis(a));
}
cout << n-maxx << endl;
}