VIJOS 1516 N连环

来源:互联网 发布:定时鼠标点击软件源码 编辑:程序博客网 时间:2024/04/28 18:21

题意

九连环应该都知道吧!
(1)第 1 环可以自由上下
(2)而上/下第 n 环时(n>1),则必须满足:
(a)第 n-1 个环在架上
(b)前 n-2 个环全部在架下
为了让大家多学点知识,特改此题

输入2个N连环的状态 保证环数小于等于100
第一行是初状态
第二行是末状态
输出一个数,需要的步数n(保证n不超过qword)

分析

九连环的数学模型实际上是格雷码

我们用一串01串表示N连环的一个状态 记最右边的环为第一个环
九连环包括两个操作
A:将最右边的数位反转 例如:00000->00001
B:将最右边的1左边的数位反转 例如:00101->00111 00100->01100

显然,A和B两种操作任何一种重复做两次,01串等价于没有修改
例如:00000–A->00001–A->00000

因此我们只有让A B操作交替执行
才能让初始串得到其他所有操作序列

这正好就是格雷码的直接排列(以二进制为0值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。)

那么只要将本问题的初末格雷码转换为二进制,两个二进制串的数值差就是答案了

而二进制和格雷码之间具有怎样的关系呢
一个二进制串数值增加1,
如果不进位只会造成最末一位数改变
如果进位将会引起该二进制串lowbit的上一位开始的一系列数位变动
如: 101010->101011 100111->101000
将这两条性质与格雷码的直接排列进行对比
我们可以发现,
格雷码串是二进制串的差分
二进制串是格雷码串的前缀

此处的差分和前缀是建立在模2意义下,证明略去

那么根据这个转换性质就可以很快地把题目解决了

代码

#include<cstdio>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fe(i,a,b) for(int i=a;i>=b;i--)unsigned long long ans;bool a[200],b[200];char c[200];int l;int main(){    scanf("%s",c+1);    l=strlen(c+1);    fo(i,1,l)        if (c[l-i+1]=='0')            a[i]=0;        else            a[i]=1;    scanf("%s",c+1);    fo(i,1,l)        if (c[l-i+1]=='0')            b[i]=0;        else            b[i]=1;    fe(i,l-1,1){        a[i]=a[i+1]^a[i];//a[i-1]^a[i];        b[i]=b[i+1]^b[i];//b[i-1]^b[i];    }    fe(i,l,1){        if ((a[i]==1)&&(b[i]==0)){            ans+=(unsigned long long)1<<(i-1);//            fe(j,i-1,1){                if ((a[j]==1)&&(b[j]==0))                    ans+=(unsigned long long)1<<(j-1);                if ((a[j]==0)&&(b[j]==1))                    ans-=(unsigned long long)1<<(j-1);                              }            printf("%I64u\n",(unsigned long long)ans);            return 0;        }        if ((b[i]==1)&&(a[i]==0)){//得到二进制串的数值差            ans+=(unsigned long long)1<<(i-1);            fe(j,i-1,1){                if ((a[j]==1)&&(b[j]==0))                    ans-=(unsigned long long)1<<(j-1);                if ((a[j]==0)&&(b[j]==1))                    ans+=(unsigned long long)1<<(j-1);            }            printf("%I64u\n",(unsigned long long)ans);            return 0;        }    }    printf("0\n");//特殊情况的输出    return 0;}

补充

n进制格雷码串同样也是n进制数串的差分

除了格雷码的直接排列外,还有一种镜射排列:
下图可以清晰地展示排列过程(来自wiki)

这里写图片描述

0 0
原创粉丝点击