您的位置:首页 > 其它

VIJOS 1516 N连环

2016-06-27 22:55 120 查看

题意

九连环应该都知道吧!

(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)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  vijos 数学