HDU汉诺塔系列
这几天刷了杭电的汉诺塔一套,来写写题解。
HDU1207 汉诺塔II
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1207 ,多柱汉诺塔问题。
先说下四柱汉诺塔的Frame算法:
(1)用4柱汉诺塔算法把A柱上部分的n- r个碟子通过C柱和D柱移到B柱上。 【F( n- r )步】
(2)用3柱汉诺塔经典算法把A柱上剩余的r个碟子通过C柱移到D柱上。 【2^r-1步】
(3)用4柱汉诺塔算法把B柱上的n-r个碟子通过A柱和C柱移到D柱上。 【F(n-r)步】
(4)依据上边规则求出所有r(1≤r≤n)情况下步数f(n),取最小值得最终解。
因此Frame算法的递归方程如下: F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n)。
通过这个方程我们能得到所有4柱汉诺塔的步骤个数,同时也有人证明了,对于四柱汉诺塔,当r=(sqrt(8*n+1)-1)/2时,能保证f(n)取得最小值。所以算法的复杂度是F(n)=O(sqrt(2*n)*2^ sqrt(2*n))。
基于四柱汉诺塔的Frame算法,我们可以引申到多柱(M柱)汉诺塔的情况,我们简称M柱汉诺塔算法:
(1)用M柱汉诺塔算法把1柱上部分的n-r个碟子通过3…M柱移到2柱上。 【M( n- r )步】
(2)用M-1柱汉诺塔算法把1柱上剩余的r个碟子通过3…M-1柱移到M柱上。 【<M-1>(r)步】
(3)用M柱汉诺塔算法把2柱上的n-r个碟子通过1柱和3…M柱移到M柱上。 【M( n- r )步】
(4)依据上边规则求出所有r(1≤r≤n)情况下步数m(n),取最小值得最终解M(n)。
综上,题目的解答可以通过方程直接得到
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
LL pow2[maxn] , r[maxn] , tower4[maxn];
int main()
{
int n;
pow2[] = ;
for(int i = ; i < maxn ; i++)
pow2[i] = pow2[i - ] << ;
for(int i = ; i < maxn ; i++) {
double tmp = (sqrt(8.0 * i + ) - ) * 0.5;
r[i] = int(tmp);
}
for(int i = ; i < maxn ; i++) {
int j = r[i];
tower4[i] = * tower4[i - j] + pow2[j] - ;
}
while(cin >> n)
{
cout << tower4[n] << endl;
}
return ;
}
HDU1207
HDU1995 汉诺塔V
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1995 ,简单题。
先贴一些关于汉诺塔的结论:
一号柱有n个盘子,叫做源柱.移往3号柱,叫做目的柱.2号柱叫做中间柱.
全部移往3号柱要f(n) =(2^n)- 1次.
最大盘n号盘在整个移动过程中只移动一次,n-1号移动2次,i号盘移动2^(n-i)次.
1号盘移动次数最多,每2次移动一次.
第2k+1次移动的是1号盘,且是第k+1次移动1号盘.
第4k+2次移动的是2号盘,且是第k+1次移动2号盘.
......
第(2^s)k+2^(s-1)移动的是s号盘,这时s号盘已被移动了k+1次.
每2^s次就有一次是移动s号盘.
第一次移动s号盘是在第2^(s-1)次.
第二次移动s号盘是在第2^s+2^(s-1)次.
......
第k+1次移动s号盘是在第k*2^s+2^(s-1)次.
1--2--3--1叫做顺时针方向,1--3--2--1叫做逆时针方向.
最大盘n号盘只移动一次:1--3,它是逆时针移动.
n-1移动2次:1--2--3,是顺时针移动.
如果n和k奇偶性相同,则s号盘按逆时针移动,否则顺时针.
所以根据上面的知识,可知k号盘移动 2^(n - k)次
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
LL pow2[maxn];
int main()
{
int n , k , T;
pow2[] = ;
for(int i = ; i <= maxn ; i++)
pow2[i] = pow2[i - ] << ;
cin >> T;
while(T--)
{
cin >> n >> k;
cout << pow2[n - k] << endl;
}
return ;
}
HDU1995
HDU1996 汉诺塔VI
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1996 ,水题。
因为每个盘子有三个选择,所以就是3^n。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
LL pow3[maxn];
int main()
{
int n , k , T;
pow3[] = ;
for(int i = ; i <= maxn ; i++)
pow3[i] = pow3[i - ] * ;
cin >> T;
while(T--)
{
cin >> n;
cout << pow3[n] << endl;
}
return ;
}
HDU1996
HDU1997 汉诺塔VII
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1997 ,递归判断。
算法:
由于传统汉诺塔一共有三步:
(1)先把n-1个盘子从A盘借助C盘移到B盘;
(2)把最大盘n号盘从A盘移动到C盘;
(3)把n-1个盘子从B盘借助A盘移动到C盘。
所以此题如下分析:
* 首先判断是否都在A盘或者都在C盘,如果是这种情况就说明都没放或者都已经放好了,这时候返回true;
* 这时候开始考虑最大盘n号盘,根据上面可知n号盘只能在A柱或C柱上,在B柱上则返回false(因为B为中间柱);
* 如果n号盘在A上,则说明这时在进行(1)步,这时减小规模,考虑n-1号盘,移动方向为A->B(C为中间柱);
* 如果n号盘在C上,则说明此时在进行(3)步,这时减小规模,考虑n-1号盘,移动方向为B->C(A为中间柱);
* 按照上述过程递归,n为0时候返回true即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
int a[maxn] , b[maxn] , c[maxn];
bool check(int n , int a[] , int b[] , int c[])
{
if(n == )
return true;
for(int i = ; b[i] != ; i++) {
if(b[i] == n)
return false;
}
for(int i = ; a[i] != ; i++) {
if(a[i] == n)
return check(n - , a , c , b);
}
for(int i = ; c[i] != ; i++) {
if(c[i] == n)
return check(n - , b , a , c);
}
}
int main()
{
int n , m , p , q , T;
cin >> T;
while(T--)
{
memset(a , , sizeof(a));
memset(b , , sizeof(b));
memset(c , , sizeof(c));
scanf("%d" , &n); scanf("%d" , &m);
for(int i = ; i <= m ; i++)
scanf("%d" , &a[i]);
scanf("%d" , &p);
for(int i = ; i <= p ; i++)
scanf("%d" , &b[i]);
scanf("%d" , &q);
for(int i = ; i <= q ; i++)
scanf("%d" , &c[i]); if(m == n || q == n || check(n , a , b , c))
printf("true\n");
else
printf("false\n");
}
return ;
}
HDU1997
HDU2064 汉诺塔III
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2064 ,公式递推。
算法:
由于题目的限制,所以应该这样移动:
(1)先把n-1个盘子从A柱通过B柱移动到C柱;
(2)把最大盘第n号盘从A移动到B;
(3)把前n-1个盘子从C柱通过B柱移动到A柱;
(4)把最大盘第n号盘从B移动到C;
(5)把前n-1个盘子从A通过B移动到C。
根据上述,可以得到递推公式为:f(n) = 3 * f(n - 1) + 2 , 即 f(n) = 3 ^ n - 1
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
LL pow3[maxn];
int main()
{
pow3[] = ;
for(int i = ; i < maxn ; i++)
pow3[i] = pow3[i - ] * ;
int n;
while(cin >> n)
cout << pow3[n] - << endl;
return ;
}
HDU2064
HDU2077 汉诺塔IV
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2077 ,递推。
算法:
这道题其实是前一道加了一些变换,移动步骤:
(1)先把n-2个盘子从A柱通过B柱移动到C柱;
(2)把 n 和 n-1 号盘从A移动到B;
(3)把前n-2个盘子从C柱通过B柱移动到A柱;
(4)把 n 和 n-1 号盘从B移动到C;
(5)把前n-2个盘子从A通过B移动到C。
设n个盘子的结果为p(n),可得到递推公式: p(n) = 3 * f(n - 2) + 4 , 即p(n) = 3 ^ (n - 1) + 1 (注:这里的f(n)指的是HDU2064的结果)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
LL pow3[maxn];
int main()
{
pow3[] = ;
for(int i = ; i < maxn ; i++)
pow3[i] = pow3[i - ] * ;
int n , T;
cin >> T;
while(T--) {
cin >> n;
cout << pow3[n - ] + << endl;
}
return ;
}
HDU2077
HDU2175 汉诺塔IX
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2175 ,简单题。
算法:
根据上面的结论:第 k+1 次移动s号盘是在第 k*2^s+2^(s-1) 次
所以如果有满足 m % (2 ^ s) == 2 ^ (s-1)即说明这次移动的是s号盘。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
int main()
{
int n , k;
LL m , s , t;
while(~scanf("%d %I64d" , &n , &m) && n && m)
{
s = ;
t = s << ;
for(k = ; k <= n ; k++) {
if(m % t == s)
break;
s = t;
t <<= ;
}
printf("%d\n" , k);
}
return ;
}
HDU2175
HDU2184 汉诺塔VIII
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2184 , 递归。
算法:
还是先说传统汉诺塔的三步:
(1)先把n-1个盘子从A盘借助C盘移到B盘;
(2)把最大盘n号盘从A盘移动到C盘;
(3)把n-1个盘子从B盘借助A盘移动到C盘。
根据上题的那个结论:第 k+1 次移动s号盘是在第 k*2^s+2^(s-1) 次,所以n号盘如果移动的话,说明m >= 2 ^ (n-1)。所以对第n号来说有下面两种情况:
* 如果m >= 2 ^ (n - 1),说明这时最大号盘已经移动,即n号盘此时在C上,此时正在进行的是(3),这时减小规模为n-1,m -= 2 ^ (n-1);
* 如果m < 2 ^ (n - 1),说明这时最大号盘还未移动,n号盘此时在A上,此时正在进行的是(1),这时减小规模为n-1,m不变;
递归调用该过程,n == 0返回。
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
#define eps 1e-8
#define INF 1e8
const int maxn = ;
LL pow2[maxn];
vector <int> a , b , c;
void move(int n , LL m , vector <int> &a , vector <int> &b , vector <int> &c)
{
if(n == )
return;
if(m >= pow2[n - ]) {
c.push_back(n);
m -= pow2[n - ];
move(n - , m , b , a , c);
} else {
a.push_back(n);
move(n - , m , a , c , b);
}
}
void print(vector <int> a)
{
printf("%d" , a.size());
if(!a.empty()) {
for(int i = ; i < a.size() ; i++)
printf(" %d" , a[i]);
}
puts("");
}
int main()
{
int n , T;
LL m;
pow2[] = ;
for(int i = ; i < maxn ; i++)
pow2[i] = pow2[i - ] << ;
cin >> T;
while(T--)
{
scanf("%d %I64d" , &n , &m);
a.clear();
b.clear();
c.clear();
move(n , m , a , b , c);
print(a);
print(b);
print(c);
}
return ;
}
HDU2184
HDU2511 汉诺塔 X
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2511 ,根据结论来。
算法:
第k+1次移动s号盘是在第k*2^s+2^(s-1)次。
1--2--3--1叫做顺时针方向,1--3--2--1叫做逆时针方向。
最大盘n号盘只移动一次:1--3,它是逆时针移动。
n-1移动2次:1--2--3,是顺时针移动。
如果n和k奇偶性相同,则s号盘按逆时针移动,否则顺时针。
所以如果 m == k*2^s+2^(s-1),说明此时是第 k+1 次移动s号盘,这时再根据k+1和n的奇偶性判断移动方向。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
const int maxn = ;
int main()
{
int n , T;
LL m;
cin >> T;
while(T--)
{
scanf("%d %I64d" , &n , &m);
LL s , t , l , k;
s = ; t = s << ;
for(l = ; l <= n ; l++) {
if(m % t == s) break; //满足 k*(2^l) + 2^(l-1) == m
s = t; //s 和 t分别表示 2^(l-1)、2^l
t <<= ;
}
printf("%d " , l); //此时移动的是l号盘
k = m / t + ; //第k次移动l号盘
if(n % == l % ) { //逆时针
if(k % == ) printf("2 1\n");
if(k % == ) printf("1 3\n");
if(k % == ) printf("3 2\n");
} else { //顺时针
if(k % == ) printf("3 1\n");
if(k % == ) printf("1 2\n");
if(k % == ) printf("2 3\n");
}
}
return ;
}
HDU2511
HDU汉诺塔系列的更多相关文章
- HDU 汉诺塔系列
做了这一系列题,表示对汉诺塔与这一系列递推理解加深了 经典汉诺塔:1,2,...,n表示n个盘子,数字大盘子就大,n个盘子放在第1根柱子上,按照从上到下 从小到大的顺序排放,过程中每次大盘都不能放在小 ...
- 汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI
汉诺塔 汉诺塔II hdu1207: 先说汉若塔I(经典汉若塔问题),有三塔.A塔从小到大从上至下放有N个盘子.如今要搬到目标C上. 规则小的必需放在大的上面,每次搬一个.求最小步数. 这个问题简单, ...
- HDU 2077 汉诺塔IV (递推)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2077 还记得汉诺塔III吗?他的规则是这样的:不允许直接从最左(右)边移到最右(左)边(每次移动一定是 ...
- HDU 2064 汉诺塔III (递推)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2064 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到 ...
- HDU 1207 汉诺塔II (找规律,递推)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1207 汉诺塔II Time Limit: 2000/1000 MS (Java/Others) ...
- HDU 1996汉诺塔VI
题目: n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱 子从下往上的大小仍保持 ...
- 汉诺塔III HDU - 2064
汉诺塔III HDU - 2064 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘全部移到右 ...
- HDU 2064 汉诺塔III
汉诺塔III Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...
- HDU 2064 汉诺塔III(递归)
题目链接 Problem Description 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘 ...
随机推荐
- asp.net刷新本页面的六种方法总结
第一: private void Button1_Click( object sender, System.EventArgs e ) { Response.Redirect( Reque ...
- 实现one hot encode独热编码的两种方法
实现one hot encode的两种方法: https://stackoverflow.com/questions/37292872/how-can-i-one-hot-encode-in-pyth ...
- 边界提取_MATLAB
下面是利用腐蚀算法进行边界提取,即原图减去腐蚀后的图得到边界 f=imread('D:/picture/ZiXia.jpg'); figure; subplot(,,); imshow(f); tit ...
- click点击事件先后顺序的问题
//页面加载时,每秒钟调用一次var times = setInterval("loadFlws()","1000"); function loadFlws() ...
- JAVA String.format()的使用
常规类型的格式化 String类的format()方法用于创建格式化的字符串以及连接多个字符串对象.熟悉C语言的同学应该记得C语言的sprintf()方法,两者有类似之处.format()方法有两种重 ...
- Python Day22
Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类 ...
- CSS基本
CSS选择器优先级:从高到低 无条件优先的属性只需要在属性后面使用!important,但是IE6不支持.解决办法,IE6是单个支持的. 例如: 在IE6中需要这样写 .className{color ...
- 多实例:MySQL系列之二
MySQL的多实例配置 在一台物理机中需要多个测试环境,那么就需要用到了搭建数据库的多个实例,多个实例的意思就是运行多份程序,实例与实例之间没有影响.要注意监听的端口需要不同. 环境:CentOS ...
- 打开powerDesigner时,创建table对应的自动生成sql语句没有注释
在创建pdm时由于需要在name列填写的是以后要在表中创建的注释信息,comment中则写的说明信息字数比较多.默认情况下在生成建表sql时不能将name生成注释信息,进行如下设置可以讲name生成注 ...
- gulp前端自动化构建工具
博主不易,不求赞赏,希望把自己遇到的难点写出来,以及希望自己能有能力写出一篇不错的博文. 前端构建工具本人 bootstrap+jquery用gulp vue+element 用webpack 引文 ...