VIJOS 1516 N连环

本文介绍了一种解决九连环问题的方法,利用格雷码的特性将问题转化为二进制串的操作,并通过代码实现了从初始状态到目标状态所需的最小步骤数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

九连环应该都知道吧!
(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)

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值