算法训练 Hanoi问题

Description

  如果将课本上的Hanoi塔问题稍做修改:仍然是给定N只盘子,3根柱子,但是允许每次最多移动相邻的M只盘子(当然移动盘子的数目也可以小于M),最少需要多少次?
  例如N=5,M=2时,可以分别将最小的2个盘子、中间的2个盘子以及最大的一个盘子分别看作一个整体,这样可以转变为N=3,M=1的情况,共需要移动7次。

Input

输入描述:
  输入数据仅有一行,包括两个数N和M(0<=M<=N<=8)
输入样例:

5 2

Output

输出描述:
  仅输出一个数,表示需要移动的最少次数
输出样例:

7

Hint

  HINT:时间限制:1.0s 内存限制:512.0MB

Source

  蓝桥杯练习系统 ID: 108 原题链接: http://lx.lanqiao.cn/problem.page?gpid=T108

Submit

1、复习

  先复习传统的 Hanoi问题 的实现,与本题不同在于每次只允许移动一个盘子 。
  提一句这个算法涉及了递归和分治知识点 。

#include <iostream>
using namespace std;

void move(char &x, char &y, int &ans) {
	//执行移动盘子的操作 
	cout << x << "->" << y << endl;
	ans++;
} 

void hanoi(int n,char one, char two, char three, int &ans) {
	//将n个盘从one座借助two座,移到three座
	if(n==1) {//只有 1 个盘直接移,不需要借助 
		move(one, three, ans);
	}
	else {
		hanoi(n-1, one, three, two, ans);
		move(one, three, ans);
		hanoi(n-1, two, one, three, ans);
	}
}

2、思路一:

  递归 + 分治
  把 n 个盘中的每 m个想成一个整体,就变成了传统的只能一次移动一个盘的 Hanoi问题

#include <iostream>
#include <cmath>
using namespace std;

void move(char &x, char &y, int &ans) {
	//执行移动盘子的操作 
	//cout << x << "->" << y << endl;
	ans++;
} 

void hanoi(int n,char one, char two, char three, int &ans, int &m) {
	//将n个盘从one座借助two座,移到three座
	if(n<=m) { //m个想成一个整体 
		move(one, three, ans);
	}
	else {
		hanoi(n-m, one, three, two, ans, m);
		move(one, three, ans);
		hanoi(n-m, two, one, three, ans, m);
	}
}

3、思路二:

  公式
  我们知道传统 Hanoi问题 求移动次数有公式,就是:移动 n 个盘子要经历
  那么对于本题而言,这个公式是什么呢?
  如题目所言,N=5,M=2时,可以分别将最小的2个盘子、中间的2个盘子以及最大的一个盘子分别看作一个整体,这样可以转变为N=3,M=1的情况,共需要移动7次。
  又如n=5,m=3时,三盘合1个,剩余的2盘合1,也可视为传统的移n=2问题。
  依据这个道理也就有了:移动 n 个盘子,并限制每次最多移动 m 要经历
  利用cmath函数库:四舍五入RoundEx() 向上取整ceil() 向下取整floor()

#include <iostream>
#include <cmath>
using namespace std;

int main() {
	int n, m;
	cin >> n >> m;

	cout << pow(2, ceil(n/(double)m))-1;

	return 0;
}

posted on 2018-12-28 23:54  yejifeng  阅读(649)  评论(0)    收藏  举报

导航