[题解][笔记]lgP1368&最小表示法

[题解][笔记]lgP1368&最小表示法

原题链

算法用途

这个题就是要求在一个循环同构串中找出字典序最小的排列

算法过程

首先,因为这个数列可以把第一个元素一道最后一个去,所以我们把这个序列倍长,这样就不用对指针取模啥的,更加方便.
接着来讲算法的主体.首先我们先假设有一个排列:\(7 5 3 3 3 1 4 6\),把这个排列倍长就变成:\(7 5 3 3 3 1 4 6 7 5 3 3 3 1 4 6\),然后最小表示法是有两个指针(不能相等),先假设两个指针分别为\(pos1\),\(pos2\),那么这\(pos1\)指针就代表的是倍长后的序列从\(pos1\)作为开头,到\(pos1 - 1\)作为结尾的序列,\(pos2\)同理.接着我们先考虑\(pos1\)指针所指的数大于\(pos2\)指针所指的数的情况,这时显然以\(pos2\)为开头的排列更优所以更新\(pos1\),就是把\(pos1+1\),如果\(pos1\)所指的元素小于\(pos2\)所指的元素也是同理.
接下来重点讲讲如果\(pos1\)\(pos2\)所指的元素是一样的怎么办.对于上面的序列,当\(pos1=3\),\(pos2 = 4\)时就出现了两个指针所指的元素相等的情况,这时我们无法判断两个指针谁代表的序列更优,所以\(pos1\)\(pos2\)都往后跳一个,知道出现两个指针指着的元素不相等的时候为止,在举的例子中当两个指针向后跳了\(2\)次后所指的元素就不同了(实际实现的时候并不是直接变动指针的值,而是让指针加上一个向后移动的变化量).此时,\(pos1=3+2=5,pos2=4+2=6\),我们可以发现\(pos2\)所指的值小于\(pos1\)所指的值,所以\(pos2\)更优一些,并且在下标为\(3~5\)为开头的序列都不会是最有的,因为通过刚才的匹配我们得知下标为\(6\)为开头的才是最有的所以把\(pos1\)指针移动到下标\(6\)处.
这就是算法的基本过程,还没看懂的可以看看代码有一点点注释

代码实现

#include <bits/stdc++.h>
using namespace std;
int n;
int a[3000010];
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		scanf("%d",&a[i]);
		a[i + n] = a[i];//把原序列倍长
	}
	int pos1 = 1,pos2 = 2,delta = 0,pos;
	while(pos1 <= n && pos2 <= n){
		delta = 0;//向后移动的变化量
		while(a[pos1 + delta] == a[pos2 + delta])//如果指针指向的元素大小相等就一起往后跳
			delta++;
		if(a[pos1 + delta] > a[pos2 + delta])pos1 += delta + 1;//跳到当前的最优解
		else pos2 += delta + 1;//调到当前的最优解
		if(pos1 == pos2)pos2++;//两个指针不能相等
	}
	pos = min(pos1,pos2);//因为有可能会超过n所以去最小值
	for(int i = pos;i <= n + pos - 1;i++){
		printf("%d ",a[i]);
	}
	return 0;
}
posted @ 2020-10-28 16:52  czyczy  阅读(88)  评论(0编辑  收藏  举报