2020牛客暑期多校训练营(第五场)D- Drop Voicing

Statement

  • 有两种操作
  • o p e r a t i o n 1 operation-1 取倒数第二个移到首位
  • o p e r a t i o n 2 operation-2 把第一个移到末尾
  • 要求多次 o p e r a t i o n 1 operation-1 连在一起时次数最小

Solution 1

  • o p e r a t i o n 2 operation-2 易得这是一个随便转的圆盘
  • 而圆盘旋转并不改变元素的相对位置
  • 只有 o p e r a t i o n 1 operation-1 改变了相对位置

  • 当多次执行 o p e r a t i o n 1 operation-1
  • 每一次挪动的时候,其它数字被挤开相应往顺时针挪动
  • 看上去很复杂
  • 我们把移动的所有数字 a x . . . a n 1 a_x...a_n-1 看做一个整体
  • 等价于把这个整体移到 a n a_n 后面 ( a 1 ) (a_1前面)
  • 相对的,操作等价于将 a n a_n 移到第 x x
  • 进一步,所谓多次operation-1连在一起其实就是将任意数字移动到任意位置
  • 接下来就很简单了
  • 我们求出原序列的最长上升子序列
  • 对于不在最长上升子序列里的数,我们每一个数花一个多次operation-1连在一起 移动到正确的位置就好了

Solution 2

  • 当你看到 ( 2 n 500 ) (2 \leq n \leq 500) 的数据范围
  • 容易猜出时复 O ( N 3 ) O(N^3) O ( N 2 l o g N ) O(N^2logN)
  • 如果你有欧皇血统就像同机房的某大佬
  • 就能猜出算法为最长上升子序列
  • 如果你能想到这一步,恭喜你得到了朱子百家真传——“看数据,猜算法”

Code( O ( N 3 ) O(N^3) )

#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=510;
using namespace std;
int n,ans,a[N],dp[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	for(int c=1;c<=n;c++){
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++){
			dp[i]=1;
			for(int j=1;j<i;j++)
				if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1);
			ans=max(ans,dp[i]);	
		}int tmp=a[1];
		for(int i=1;i<n;i++) a[i]=a[i+1];
		a[n]=tmp;
	}printf("%d\n",n-ans);
}

猜你喜欢

转载自blog.csdn.net/ding_ning123/article/details/107583456
今日推荐