[题解][笔记]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;
}