问题
https://vjudge.net/problem/UVA-517
分析
每次都循环到字典序最小的时候记录。
一共最多有
2
1
6
2^16
216种,因为每种状态都变到字典序最小的时候,所以实际的远小于
2
1
6
2^16
216,使用map保存。
先求出一次循环时的长度T,起始点a,终止点b,T=b-a, 答案是第(s-a)%T+a种状态。
注意序号:输入时数组中是 0,1,2…i,i+1,i+2,转到二进制后是0,1,2…i-2,i-1,i,i+1, 不能是i+2,i+1,i,i-1,i-2(因为这样就反向了,相当于翻转了)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn=(1<<15)+100;
int n,s,kase=0,vis[maxn],rule[8];
char a[100];
vector<int> rec;
inline int minlexico(int temp){
int t=temp,flag=0;
for(int i=0;i<n;++i){
flag=temp>>(n-1);
temp&=(1<<(n-1))-1;
temp<<=1;
temp|=flag;
if(temp<t) t=temp;
}
return t;
}
inline int getindex(int c1,int c2,int c3){
return (c1<<2)+(c2<<1)+c3;
}
int solve(int start){
int k=1,op=0,ed=0;
for(;;++k){
int rewriting=0;
for(int i=0;i<n;++i){
int c1=((i+2)>=n?(start&(1<<(i+2-n))):(start&(1<<(i+2))))?1:0;
int c2=(start&(1<<i))?1:0;
int c3=((i-1)>=0?(start&(1<<(i-1))):(start&(1<<(i-1+n))))?1:0;
rewriting|=(rule[getindex(c1,c2,c3)]<<i);
}
rewriting=minlexico(rewriting);
rec.push_back(rewriting);
if(s==k) return rewriting;
if(vis[rewriting]!=-1){
op=vis[rewriting];
ed=k;
break;
}
start=rewriting;
vis[rewriting]=k;
}
return rec[(s-op)%(ed-op)+op];
}
int main(void){
while(scanf("%d",&n)==1){
rec.clear();
memset(vis,-1,sizeof(int)*((1<<n)+10));
int start=0;
scanf("%s",a);
for(int i=0;i<n;++i){
if(a[i]=='b'){
start|=(1<<(n-1-i));
}
}
start=minlexico(start);
vis[start]=0;
rec.push_back(start);
char ch[10];
for(int i=0;i<8;++i) {
scanf("%s",ch);
rule[getindex(ch[0]-'a',ch[1]-'a',ch[2]-'a')]=ch[3]-'a';
}
scanf("%d",&s);
int ans=solve(start);
for(int i=n-1;i>=0;--i){
if(ans&(1<<i)){
putchar('b');
}else putchar('a');
}
printf("\n");
}
return 0;
}