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个圆盘构成的塔.目的是将最左边杆上的盘 ...
随机推荐
- Thinkphp通过phpqrcode实现网址验证码
第一步: phpqrcode下载第三方扩展包,http://phpqrcode.sourceforge.net/ 第二步: 将扩展包放到/ThinkPHP/Library/Vendor/下的Phpqr ...
- C# 中介者模式
中介者模式 中介者模式,定义了一个中介对象来封装一系列对象之间的交互关系.中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为. 结构: 中介者模式设计两个 ...
- Wannafly summer camp Day2I(思维)
#include<bits/stdc++.h>using namespace std;int a[1000007],b[1000007],c[1000007];int find_max(i ...
- uva 1608 不无聊的序列
uva 1608 不无聊的序列 紫书上有这样一道题: 如果一个序列的任意连续子序列中都至少有一个只出现一次的元素,则称这个序列时不无聊的.输入一个n个元素的序列,判断它是不是无聊的序列.n<=2 ...
- hdu 5693 D Game
D Game HDU - 5693 众所周知,度度熊喜欢的字符只有两个:B 和D. 今天,它发明了一个游戏:D游戏. 度度熊的英文并不是很高明,所以这里的D,没什么高深的含义,只是代指等差数列[(等差 ...
- any cpu ×86 ×64
On a 32-bit machine: Any CPU: runs as a 32-bit process, can load Any CPU and x86 assemblies, will ge ...
- dbms_xplan的display_cursor查看执行计划
准备工作: SQL> conn sys/root as sysdba Connected. SQL> grant select on v_$sql_plan to scott; Grant ...
- dbms_xplan的display查看执行计划
DBMS_XPLAN包包括一系列函数,主要是用于显示SQL语句的执行计划,且不同的情形下使用不同的函数来显示,如预估的执行计划则使用 display函数,而实际的执行计划则是用display_curs ...
- string类型的方法
var str1 = ' this is string1 '; var str2 = 'this is string2'; str1.indexOf('s'); //args:string retur ...
- PHPExcel探索之旅---阶段四 导入文件
步骤就是:实例化excel读取对象=> 加载excel文件 => 读取excel文件(全部读取.逐行读取) <?php header("Content Type :text ...