参考了别人的代码的总结
1.四柱汉诺塔问题和n柱汉诺塔问题
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1207
参考了别人的题解:http://www.cnblogs.com/fanzhidongyzby/archive/2012/07/28/2613173.html
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double f[70];
void init() {
f[1] = 1;
f[2] = 3;
for(int i = 3; i <= 65; i++) {
double Max = f[i - 2] * 2 + 3;
for(int j = 1; j < i; j++)
Max = min(Max, 2 * f[i - j] + pow(2,j) - 1);
f[i] = Max;
}
}
int main() {
init();
int n;
while(scanf("%d", &n) == 1) {
printf("%d\n", (int)f[n]);
}
return 0;
}
2.计算某个盘子被移动的次数
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1995
解题思路:第n个盘子移动的步数肯定是1
移动第n个盘子到c柱之后,第n-1个盘子移到c柱子之前,第n -1个盘子移动了1次,然后不考虑第n个盘子,第n-1个盘子还需要移动1次,所以总的移动了2次。
第一次第n-1个盘子到b柱时,第n-2个盘子已经移动了2次。然后将第n-1个盘子看成第n个盘子,第n-2个盘子看成第n-1个盘子,就可以递推下去了
如果不理解的话,可以动手画画,画一下就知道了
#include<cstdio>
long long ans[65];
int main() {
ans[1] = 1;
for(int i = 2; i <= 60; i++)
ans[i] = ans[i - 1] * 2;
int test, x, y;
scanf("%d", &test);
while(test--) {
scanf("%d%d", &x, &y);
printf("%I64d\n", ans[x - y + 1]);
}
return 0;
}
3.无规则汉诺塔
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1996
解题思路:每个盘子都有三种摆放方式,所以共有3^n种
#include<cstdio>
int main() {
long long ans[31];
int test, n;
ans[1] = 3;
for(int i = 2; i <= 30; i++)
ans[i] = ans[i-1] * 3;
scanf("%d", &test);
while(test--) {
scanf("%d", &n);
printf("%I64d\n",ans[n]);
}
return 0;
}
4.状态查询
题目http://acm.hdu.edu.cn/showproblem.php?pid=1997
解题思路:按照最优汉诺塔的规则
1. 将A柱上的n-1个盘子放到B柱子上
2. 将A柱子上的第n个盘子移动到C柱子上
3. 最后将B柱子上的n-1个盘子移动到C柱子上
这三个形成递归过程,也就是说,第n个盘子所在的位置要么是在A柱子上,要么就是在C柱子上,且是将A柱上的n移动到C柱子上了
设一个函数f(int n,int * a, int *b, int *c)
代表三个柱子,如果第n个盘子在b柱子上,表示false了
如果第n个盘子在a柱子上,下一步就是要把第n-1个盘子从a柱子移动到c柱子上
如果第n个盘子在c柱子上,下一步就是要把b柱子上的第n-1个盘子移动到c柱子上了
#include<cstdio>
#include<cstring>
#define maxn 70
int num[3][maxn];
bool solve(int n, int *one, int *two, int *three) {
if(two[0] == n)
return 0;
else if(one[0] == n)
return solve(n - 1, one + 1, three, two);
else if(three[0] == n)
return solve(n - 1, two, one, three + 1);
return 1;
}
int main() {
int test, n;
scanf("%d", &test);
while(test--) {
int n, m;
scanf("%d", &n);
for(int i = 0; i < 3; i++) {
scanf("%d", &m);
for(int j = 0; j < m; j++)
scanf("%d", &num[i][j]);
num[i][m] = -1;
}
printf("%s\n", solve(n,num[0],num[1],num[2]) ? "true" :"false");
}
return 0;
}
5.不规则移动
A.
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2064
解题思路:
1.先把A柱子上的n-1个盘子移动到C柱子上
2.再把A柱子上的第n个盘子移动到B柱子上
3.再把C柱子上的n-1个盘子移动到A柱子上(和第一步对称)
4.把B柱子上的第n个盘子移动到C柱子上
5.最后把A柱子上的n-1个盘子移动到C柱子上
#include<cstdio>
int main() {
long long ans[40];
int n;
ans[1] = 2;
for(int i = 2; i <= 35; i++)
ans[i] = ans[i - 1] * 3 + 2;
while(scanf("%d", &n) == 1) {
printf("%I64d\n", ans[n]);
}
return 0;
}
B.
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2077
解题思路:其实我的思路蛮水的,强制推出
假设有数组
f数组纪录的是将A柱上的n个移动到C柱上,或者将C柱上的n个移动到A柱上,和上一题的表示是一样的
d数组纪录的是将A柱上的n个移动到B柱上需要的步数
可得到递推公式
d[n] = f[n – 1] + 1 + f[n – 2] + 1 + d[n – 2]
表示先将A柱上的n-1个移动到C柱子上,再将第n个从A柱移动到B柱上,再将C柱子上的n-2个盘子移动到A柱子上,再将第n-1个盘子移动到B柱子上,最后再将A柱子上的n-2个盘子移动到B柱子上
e数组纪录的是将B柱子上的盘子移动到C柱子上
可得到递推公式
e[n] = e[n – 2] + 1 + f[n – 2] + 1 + f[n – 1]
先讲B柱子上的n-2个盘子移动到C柱子上,再将第n-1个盘子移动到A柱子上,再将C柱子上的n-2个盘子移动到A柱子上,再将B柱子上的第n个盘子移动到C柱子上,最后减A柱子上的n-1个盘子移动到C柱子上
g数组纪录的是符合所有规则的移动次数
得递推公式
g[n] = f[n-2] + 1 + f[n-3] + 1 + d[n-3] + 2 + e[n-3] + 1 + f[n-3] + 1 + f[n-2]
具体的自己思考了
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 25
int main() {
long long f[maxn], g[maxn], d[maxn], e[maxn];
f[1] = 2;
for(int i = 2; i <= 20; i++)
f[i] = 3 * f[i - 1] + 2;
d[1] = 1, d[2] = 4;
for(int i = 3; i <= 20; i++)
d[i] = f[i - 1] + f[i - 2] + 2 + d[i - 2];
e[1] = 1, e[2] = 4;
for(int i = 3; i <= 20; i++)
e[i] = e[i - 2] + 2 + f[i - 2] + f[i - 1];
g[1] = 2, g[2] = 4, g[3] = 10;
for(int i = 4; i <= 20; i++) {
g[i] = 2 * f[i - 2] + 2 * f[i - 3] + 6 + d[i - 3] + e[i - 3];
g[i] = min(g[i],f[i]);
}
int test, n;
scanf("%d", &test);
while(test--) {
scanf("%d", &n);
printf("%lld\n", g[n]);
}
return 0;
}
6.计算第n步移动的是哪个盘子
http://acm.hdu.edu.cn/showproblem.php?pid=2175
解题思路:
第一个盘子在第1步的时候会被移动
只有两个盘子的情况下,第一个盘子在第一步的时候会被移动,然后在第3步的时候会被移动到,也就是在盘子2移动到C柱子的后的
只有三个盘子的情况下,前面的移动和两个盘子的移动是一样的,然后第一个盘子在第5步的时候会被移动到,然后移动的步骤和两个的情况下又是一样的
也就是说第一个盘子的移动是1,3,5,7…
第二个盘子的移动是2,6,10,14…
第三个盘子的移动是4,12,20…
以此递归的
#include<cstdio>
int main() {
long long ans[64];
ans[1] = 1;
for(int i = 2; i <= 63; i++)
ans[i] = ans[i - 1] * 2;
long long n, m;
while(scanf("%lld%lld", &n, &m) == 2 && n + m) {
for(int i = 1; i <= n; i++) {
long long t = m / ans[i];
if(t % 2 == 1 && m % ans[i] == 0) {
printf("%d\n", i);
break;
}
}
}
return 0;
}
7.每根柱子的状态
A. http://acm.hdu.edu.cn/showproblem.php?pid=2184
解题思路:先纪录把n个盘子从A柱子移动到C柱子的所需要步数,如果当前的步数大于把n个盘子从A柱子移动到C柱子的所需步数,就表示可以把n个盘子移动到B柱子,然后把第n+1个盘子移动到C柱子上,就表示第n+1个盘子在C柱子上了,以此递归下去
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
int row[3][64];
long long ans[64], m;
ans[1] = 1;
for(int i = 2; i <= 63; i++)
ans[i] = ans[i - 1] * 2 + 1;
int *A, *B, *C, test, n;
scanf("%d", &test);
while(test--) {
scanf("%d%lld", &n, &m);
A = row[0], B = row[1], C = row[2];
*A = 1, *B = 1, *C = 1;
while(n--) {
if(m <= ans[n]) {
*(A + *A) = n + 1;
(*A)++;
swap(B,C);
}
else {
*(C + *C) = n + 1;
(*C)++;
swap(A,B);
m -= (ans[n] + 1);
}
}
for(int i = 0; i < 3; i++) {
printf("%d", row[i][0] - 1);
for(int j = 1; j < row[i][0]; j++)
printf(" %d", row[i][j]);
printf("\n");
}
}
return 0;
}
B.http://acm.hdu.edu.cn/showproblem.php?pid=2511
解题思路:纪录要在第几步才能移动第n个盘子
1.如果当前步骤数等于要移动第n个盘子的步数,就表示刚好把第n个盘子从A柱子移动到C柱子
2.如果当前步骤数大于要移动第n个盘子的步数,就把第n个盘子从A柱子移动到C柱子,再继续递归
3.如果当前步骤数小于要移动第n个盘子的步数,就表示第n个盘子不用移动,接着递归,把第n-1个盘子从A柱子移动到B柱子
#include<cstdio>
long long ans[64];
void move(int n, long long m, int s, int e) {
int mid = 6 - s - e;
long long tmp = ans[n];
if(m == tmp) {
printf("%d %d %d\n", n, s, e);
return ;
}
if(m > tmp) {
move(n - 1, m - tmp, mid , e);
}
else
move(n - 1, m, s, mid);
}
int main() {
ans[1] = 1;
for(int i = 2; i <= 63; i++)
ans[i] = ans[i - 1] * 2;
int n, test;
long long m;
scanf("%d", &test);
while(test--) {
scanf("%d%lld", &n, &m);
move(n, m, 1, 3);
}
return 0;
}