动规之四柱汉诺塔问题

版权声明:写的不对的地方希望大家能“狠狠地”指出 https://blog.csdn.net/qq_37006625/article/details/84788940

四柱汉诺塔问题

首先我们先回忆一下经典的汉诺塔问题:
问题描述
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
在这里插入图片描述

既然都介绍了,我们就来写一下这个三柱汉诺塔问题的一个核心代码吧

void move(int i,int j)
{
	cout << i << " -> " << j << endl;
	num ++ ;
}

void hanoi(int n,int a,int b,int c)
{
	if(n == 0)
		return;
	else{
		hanoi(n - 1,a,c,b);
		move(a,c);
		hanoi(n - 1,b,a,c);
	}
}

好了,我们介绍一下四柱汉诺塔问题。其实问题很简单,就是再加一根柱子,把A上的盘子,经过B和C移动到D上去。
在这里插入图片描述

分析
k个盘子先从A上经过C,D移动到B上去,然后再将下面n-k个盘子经过C移动到D上去,
再将之前的k个盘子经过A,C移动到D上去,完成。
我们采用动态规划的思想
dp[i]表示当盘子数为 i时最小的移动次数
最优子结构

dp[i] = pow(2, i - j) - 1 + 2 * dp[j];从j处断开 j <= i

即先把j个盘子做一次四柱汉诺塔,再把i-j个盘子做一次三柱汉诺塔,再把j个盘子做一次四柱汉诺塔。
(其中pow(2, i - j) - 1的意思是i-j个盘子用三个柱子移动时的次数,是固定的)

/*
n个盘子的四柱汉诺塔问题
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

#define MAX_N 63
#define INF 0x3f3f3f3f


using namespace std;

int dp[MAX_N];
int num;

void move(char a,char b)
{
	printf("%c --> %c\n", a, b);
	num++;
}


int find_hanoi(int n,int &k)
{
	num = 0;
	dp[1] = 1;
	dp[2] = 3;
	for (int i = 3; i <= n; i++)
	{
		for (int j = 1; j < i; j++)
		{
			long long t = pow(2, i - j) - 1 + 2 * dp[j];

			if (t < dp[i] || !dp[i])
			{
				dp[i] = t;
				k = j;

			}
		}
	}
	return dp[n];
}
int main()
{
	int n;
	int k = 0;
	cout << "请输入盘子的数目n" << endl;
	memset(dp, 0, sizeof(dp));
	cin >> n;
	cout << "移动" << n << "个盘子最少需要的次数为:" << find_hanoi(n, k) << endl;
	cout << "断开位置:" << k << endl;
	
	return 0;
}

下面的代码就加了一个内容,输出移动次序,并且验证结果是否正确。
其中我们利用了动态规划的算法,计算出了n个盘子的情况下最优的断开位置

/*
n个盘子的四柱汉诺塔问题
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

#define MAX_N 63
#define INF 0x3f3f3f3f


using namespace std;

int dp[MAX_N];
int num = 0;
int myfind[MAX_N];

int find_hanoi(int n)
{
	num = 0;
	dp[1] = 1;
	dp[2] = 3;
	for (int i = 3; i <= n; i++)
	{
		for (int j = 1; j < i; j++)
		{
			long long t = pow(2, i - j) - 1 + 2 * dp[j];

			if (t < dp[i] || !dp[i])
			{
				dp[i] = t;
				myfind[i] = j;

			}
		}
	}
	return dp[n];
}

void move(char i,char j)
{
	cout << i << " -> " << j << endl;
	num ++ ;
}

void hanoi03(int n,int a,int b,int c)
{
	if(n == 0)
		return;
	else{
		hanoi03(n - 1,a,c,b);
		move(a,c);
		hanoi03(n - 1,b,a,c);
	}
}

void hanoi04(int n, int k,int a,int b,int c,int d)
{

	if(n == 1){
		move(a,d);
		return;
	}
	if(n == 2){
		move(a,c);
		move(a,d);
		move(c,d);
		return;
	}
	if(k == 0)
		return;
	hanoi04(k,myfind[k],a,c,d,b);
	hanoi03(n-k,a,c,d);
	hanoi04(k,myfind[k],b,a,c,d);
	
}

int main()
{
	int n;
	int k = 0;
	char a = 'A';
	char b = 'B';
	char c = 'C';
	char d = 'D';
	memset(dp, 0, sizeof(dp));
	memset(myfind,0,sizeof(myfind));
	cout << "请输入盘子的数目n" << endl;
	cin >> n;
	int index = find_hanoi(n);
	cout << "移动" << n << "个盘子最少需要的次数为:" << index << endl;
	cout << "移动次序****" << endl;
	hanoi04(n,myfind[n],a,b,c,d); 
	if(num == index){
		cout << "验证正确" << endl;
	}
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37006625/article/details/84788940