Vijos 1925 老爷爷与老奶奶

来源:互联网 发布:90年代网络歌曲 编辑:程序博客网 时间:2024/05/05 00:39

【题意】给定长度为n的数列,每次将最后的元素调到最前,问最少多少次能成为不下降序列(2<=n<=100000)

【分析】

①设原数列为A,A的操作具有周期性,因为调了n次回到原型

②构造数列B,使得B的长度为n+n-1,对于1<=i<=n,B[i]=A[i],对于n<i<2n,B[i]=A[i-n],例如:

A数列为3    4    5    1    2

B数列为     3    4    5    1    2    3    4    5    1,

现在的目标就是在A,B间建立联系

③A数列操作后与B数列长度为n的连续子序列一一对应

④若B数列以i开头的长度为n的连续子序列为不下降子序列,则A数列最少进行(n-i+1)%n次操作后为不下降子序列

⑤所以除非B的开头i=1,答案为0,否则B的开头越后,答案越小

⑥这样就可以使用贪心算法,从头到尾扫一遍,找连续的不下降子序列,注意去不去等于的问题。

设开头为j,结尾为k,则其中存在k-n-j+1个连续子序列,注意不是1个,因为相邻两个数可以相同,例如:

A:1  1  1  1  1

B:1  1  1  1  1  1  1  1  1

[1] 当j=1时,直接返回答案0

[2] 当j^1时,因为满足⑤的性质,所以取当前最优答案n-(k-n+1)+1=n+n-k次操作

然后继续扫,因为可能还有更优的,我第一次就栽在这里;

最终答案就是最后一次连续子序列的答案~~~

【代码】

#include <cstdio>

#include <cstring>

#include <cstdlib>


using namespace std;


const int N=100001;


int n,a[N<<1],t,res=-1;


void init(void)

{

scanf("%d",&n);

for (int i=1;i<=n;i++) scanf("%d",&a[i]);

for (int i=1;i<n;i++) a[n+i]=a[i];

n=(t=n)*2-1;

}


void work(void)

{

int j;

for (int i=1;i<=n;i++)

{

for (j=i;j<n&&a[j]<=a[j+1];j++);

if (j-i+1>=t) if (i==1) {res=0;break;} else res=t+t-j;

i=j;

}

printf("%d\n",res);

}


int main(void)

{

init();

work();

return 0;

}

【小结】

①序列的周期性问题,通常用轮回构造,例如环形DP也是差不多的

②这种求连续什么的贪心线性算法要熟悉,很多时候在二分法和其他情况也会用到

③注意不下降子序列与上升子序列的区别


0 0
原创粉丝点击