这几天刷了杭电的汉诺塔一套,来写写题解。

  HDU1207 汉诺塔II

  HDU1995 汉诺塔V

  HDU1996 汉诺塔VI

  HDU1997 汉诺塔VII

  HDU2064 汉诺塔III

  HDU2077 汉诺塔IV

  HDU2175 汉诺塔IX

  HDU2184 汉诺塔VIII

  HDU2511 汉诺塔 X


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汉诺塔系列的更多相关文章

  1. HDU 汉诺塔系列

    做了这一系列题,表示对汉诺塔与这一系列递推理解加深了 经典汉诺塔:1,2,...,n表示n个盘子,数字大盘子就大,n个盘子放在第1根柱子上,按照从上到下 从小到大的顺序排放,过程中每次大盘都不能放在小 ...

  2. 汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI

    汉诺塔 汉诺塔II hdu1207: 先说汉若塔I(经典汉若塔问题),有三塔.A塔从小到大从上至下放有N个盘子.如今要搬到目标C上. 规则小的必需放在大的上面,每次搬一个.求最小步数. 这个问题简单, ...

  3. HDU 2077 汉诺塔IV (递推)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2077 还记得汉诺塔III吗?他的规则是这样的:不允许直接从最左(右)边移到最右(左)边(每次移动一定是 ...

  4. HDU 2064 汉诺塔III (递推)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2064 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到 ...

  5. HDU 1207 汉诺塔II (找规律,递推)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1207 汉诺塔II Time Limit: 2000/1000 MS (Java/Others)     ...

  6. HDU 1996汉诺塔VI

    题目: n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱 子从下往上的大小仍保持 ...

  7. 汉诺塔III HDU - 2064

    汉诺塔III HDU - 2064   约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘全部移到右 ...

  8. HDU 2064 汉诺塔III

    汉诺塔III Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  9. HDU 2064 汉诺塔III(递归)

    题目链接 Problem Description 约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下.由小到大顺序串着由64个圆盘构成的塔.目的是将最左边杆上的盘 ...

随机推荐

  1. AQS(AbstractQueuedSynchronizer)介绍-01

    1.概述 AQS( AbstractQueuedSynchronizer ) 是一个用于构建锁和同步器的框架,许多同步器都可以通过AQS很容易并且高效地构造出来.如: ReentrantLock 和 ...

  2. cinder 服务启动与请求流程源码分析

    文章以ocata版本进行分析 cinder   api 的创建和启动,和 nova   api 类似,都是通过在 api-paste.ini 中定义 app ,然后将 app 加载之后,启动 wsgi ...

  3. JS中的for....in循环 和 for ...of循环以及iterable遍历Map和Set

    for循环的一个变体是for ... in循环,它可以把一个对象的所有属性依次循环出来: var o = { name: 'Jack', age: 20, city: 'Beijing' }; for ...

  4. MCP|DYM|Quantitative mass spectrometry to interrogate proteomic heterogeneity in metastatic lung adenocarcinoma and validate a novel somatic mutation CDK12-G879V (利用定量质谱探究转移性肺腺瘤的蛋白质组异质性及验证新体细胞突变)

    文献名:Quantitative mass spectrometry to interrogate proteomic heterogeneity in metastatic lung adenoca ...

  5. 小程序组件交互 -- 传入js

    1.父组件(wxml页面)向子组件传递 在子组件中定义需要传入的属性 properties: { count: { type: Number, value: 0, observer:function( ...

  6. 对sass通过compass进行编译

    1.创建一个Compass项目:compass create myproject 其中myproject是项目名称.2.编写scss文件.3.编译成css文件:①:Compass的编译命令是:comp ...

  7. 1、python简单介绍

    写在前面:曾经与java擦肩而过,现在懊悔很深,希望自己通过学习python,熟练掌握python,来弥补曾经的愚蠢.python简单介绍 python 1989年年底诞生,截止2017年,已经是IT ...

  8. python大战机器学习——模型评估、选择与验证

    1.损失函数和风险函数 (1)损失函数:常见的有 0-1损失函数  绝对损失函数  平方损失函数  对数损失函数 (2)风险函数:损失函数的期望      经验风险:模型在数据集T上的平均损失 根据大 ...

  9. Java面向对象_常用类库api——二叉树数据结构实现

    二叉树是每个节点最多有两个子树的有序树.通常子树被称为"左子树"和"右子树". 二叉树算法的排序规则: 1.选择第一个元素作为根节点 2.之后如果元素大于根节点 ...

  10. JVM基础知识2 垃圾收集器与内存分配策略

    如何判断堆中的哪些对象可以被回收 主流的程序语言都是使用根搜索算法(GC Roots Tracing)判定对象是否存活 基本思路是:通过一系列名为“GC Roots”的对象作为起点,从这些节点开始向下 ...