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

  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

最新文章

  1. [linux]Socket编程的头文件
  2. Linux进程间通信
  3. css书写规则总结
  4. easyui tree折叠
  5. Scala之Map,Tuple
  6. WGS84经纬度坐标与web墨卡托之间的转换【转】
  7. JVM面试
  8. Linux Shell——流程控制
  9. 【Aladdin Unity3D Shader编程】之三 光照模型(二)
  10. MSSQL 备份数据库还原
  11. Android绘图机制(三)——自定义View的实现方式以及半弧圆新控件
  12. Mysql基本操作命令【转载】
  13. MySQL中间件之ProxySQL(6):管理后端节点
  14. poj 1611 The Suspects(并查集输出集合个数)
  15. testng报告-extentsReports使用-klov
  16. ubuntu 12.04 安装 openssh-server 失败,请问怎么该弄?
  17. 【Java】 剑指offer(57-1) 和为s的两个数字
  18. .NET开发微信公众号之创建自定义菜单
  19. mantis邮件设置
  20. 在Word2007,2010,2016中分栏但不换页的方法

热门文章

  1. 【创建maven-web项目-eclipse-jee-mars-2-win32-x86_64-jdk1.8】
  2. opencv使用findContours等方法出现内存损坏之类的不能调用问题
  3. Note: Bimodal Content Defined Chunking for Backup Streams
  4. vue散碎知识点学习
  5. beeline连接hive
  6. 002 Add Two Numbers 链表上的两数相加
  7. OpenStack创建实例错误解决方法
  8. 你还在为UiPath课程考试发愁吗?
  9. 前端seo基础规范
  10. a标签常用跳转