[2020牛客暑期多校训练营第五场] D. Drop Voicing 思维+LIS

题目链接: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}}
  • [第二种] Invert: p 2 , p 3 , . . . . . , p n 1 , p n , p 1 {把最低位移到最高位,如p_2,p_3,.....,p_{n-1},p_{n},p_1}

任意连续的的Drop-2操作被称为一个muti-drop,问如何操作使得排列转变为1,2,…,n,并且要求muti-drop的数量最少。

题解

我们来看这两个操作,第一种:可以看出与 p n {p_n} 无关,只是前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}}

此时队友给了我一种思路,这两种操作可以合并为一个操作,就是将最后的一个数字 p n {p_n} 插入到前(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;
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/107647559