⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 backtrackm_c.cpp

📁 通过回溯方法来解决传教士问题
💻 CPP
字号:

#include <stdio.h>
#include <stdlib.h>
#define		FAIL	((struct RULE *)-1)	//定义失败标志
//#define		M	3		//传教士总人数
//#define		C	3		//野人总人数
//#define		K	2		//船一次可以乘坐的最多人数

static int M;
static int C;
static int K;

//输入M,C,K,对不同的结果进行测试
void Input_MCK()
{	
	int a, b;
	printf("请输入传教士和野人的数目 : \n");
	scanf("%d", &a);
	printf("请输入船上最大载客量 : \n");
	scanf("%d", &b);
	M = a, C = a, K = b;

}



// 定义节点结构
struct NODE
{
	int m;						//在左岸的传教士人数
	int c;						//在左岸的野人人数
	int b;						//b=1表示船在左岸,b=0表示船在右岸
	struct NODE *pNext;			//构成链表,指向下一个元素
};

// 定义规则结构,一条规则用本次上船的传教士人数和野人人数表示
struct RULE
{
	int m;	//本次上船的传教士人数
	int c;	//本次上船的野人人数
	struct RULE *pNext;	//构成链表,指向下一个元素
};

struct RULE *g_pRuleSet = NULL;	//定义全程变量,用于存放规则集

//判断两个节点所表示的状态是否相等
int Equal(struct NODE *pNode1, struct NODE *pNode2)
{
	if (pNode1->m == pNode2->m &&
		pNode1->c == pNode2->c &&
		pNode1->b == pNode2->b) 
		return 1;
	else
		return 0;
}

//动态产生一个节点,其状态值由参数m,c,b给定
struct NODE *NewNode(int m, int c, int b)
{
	struct NODE *pNode = NULL;
	pNode = (NODE*)malloc(sizeof(struct NODE));
	if (pNode == NULL) return NULL;
	pNode->m = m;
	pNode->c = c;
	pNode->b = b;
	pNode->pNext = NULL;
	return pNode;
}

//动态产生一个规则,其值由参数m,c给定
//表示本次过河的传教士人数和野人人数 
struct RULE *NewRule(int m, int c)
{
	struct RULE *pRule = NULL;
	pRule = (RULE*)malloc(sizeof(struct RULE));
	if (pRule == NULL) return NULL;
	pRule->m = m;
	pRule->c = c;
	pRule->pNext = NULL;
	return pRule;
}

//初始化规则集
struct RULE *InitRules(void)
{
	struct RULE *pRules = NULL, *pRule = NULL;
	int i, j;

	//形成过河规则集
	for (i = 0; i <= K; i++)
	{
		for (j = 0; j <= K; j++)
		{
			if (i + j == 0 ||
				i + j > K ||
				(i != 0 && i < j)) continue;
			pRule = NewRule(i, j);
			if (pRule == NULL) return NULL;
			pRule->pNext = pRules;
			pRules = pRule;
		}
	}
	return pRules;
}

//判断给定节点是否为目标节点
int IsGoal(struct NODE *pNode)
{
	if (pNode->m == 0 &&
		pNode->c == 0 &&
		pNode->b == 0) 
		return 1;
	else 
		return 0;
}

//判断一个状态是否为安全的
/*满足在河的任何一岸,传教士人数不少于野人人数,或者只有野
人而没有传教士。对于超出参数范围的状态,也认为是不安全的*/               
int Safe(struct NODE *pNode)
{
	if (pNode->m < 0 ||
		pNode->c < 0 ||
		pNode->m > M ||
		pNode->c > M) return 0;
	if (pNode->m == M ||
		pNode->m == 0) return 1;
	return (pNode->m == pNode->c);
}

void PrintNode(struct NODE *pNode)
{
	printf("(%d, %d, %d)\n", pNode->m, pNode->c, pNode->b);
}

void PrintRule(struct RULE *pRule)
{
	printf("(%d %d)\n", pRule->m, pRule->c);
}

void PrintNodeList(struct NODE *pList)
{
	while (pList)	//依次打印链表
	{
		printf("(%d, %d, %d)\n", pList->m, pList->c, pList->b);
		pList = pList->pNext;
	}
}

void PrintRuleList(struct RULE *pList)
{
	
	if (pList == FAIL)
	{
		printf("no solution!\n");
		return;
	}
	if (pList == NULL)
	{
		printf("( )\n");
		return;
	}
	else
	{	
		printf("(0, 0, 0)\n");
	}
	/*while (pList)	//依次打印链表
	{
		pList = pList->pNext;
	}*/
}

//求链表的长度
int Length(struct NODE *pList)
{
	if (pList == NULL) return 0;
	return Length(pList->pNext) + 1;
}

//将一个节点插入到节点链表的前面
struct NODE *ConsNode(struct NODE *pNode, struct NODE *pList)
{
	pNode->pNext = pList;
	return pNode;
}

//将一个规则插入到规则链表的前面
struct RULE *ConsRule(struct RULE *pRule, struct RULE *pList)
{
	pRule->pNext = pList;
	return pRule;
}

//将规则作用于给定的节点,产生一个新节点
struct NODE *Gen(struct RULE *pRule, struct NODE *pData)
{
	if (pData->b == 1)
	{
		return NewNode(pData->m - pRule->m, pData->c - pRule->c, 0);
	}
	else
	{
		return NewNode(pData->m + pRule->m, pData->c + pRule->c, 1);
	}
}

//复制一个规则
struct RULE *CopyRule(struct RULE *pRule)
{
	return NewRule(pRule->m, pRule->c);
}

//判断一个节点是否在一个链表中
struct NODE *In(struct NODE *pNode, struct NODE *pList)
{
	if (pList == NULL) return NULL;
	if (Equal(pNode, pList)) return pList;
	return In(pNode, pList->pNext);
}

//释放动态产生的节点链表
void FreeNodeList(struct NODE *pNodeList)
{
	struct NODE *pNode = NULL;
	while (pNodeList)
	{
		pNode = pNodeList;
		pNodeList = pNodeList->pNext;
		free(pNode);
	}
}

//释放动态产生的规则链表
void FreeRuleList(struct RULE *pRuleList)
{
	struct RULE *pRule = NULL;
	if (pRuleList == FAIL) return;
	while (pRuleList)
	{
		pRule = pRuleList;
		pRuleList = pRuleList->pNext;
		free(pRule);
	}
}

//回溯算法主函数 
struct RULE *Backtrack1(struct NODE *pDataList, int bound)
{
	struct NODE *pData = NULL, *pRData = NULL, *pRDataList = NULL;
	struct RULE *pRule = NULL, *pRules = NULL, *pPath = NULL;

	pData = pDataList;	//取当前节点
	if (In(pData, pDataList->pNext)) 
	{
		return FAIL;	//如果当前节点已经出现在初始节点到当前节点的路径上,
						//则返回失败,准备回溯
	}
	if (IsGoal(pData))	//如果当前节点是目标节点
	{
		FreeNodeList(pDataList);	//释放掉路径上的所有节点
		return NULL;	//以“空”作为解路径返回,表示从目标节点到目标节点
						//不需要任何规则操作。算法成功结束
	}
	if (!Safe(pData))	//如果当前节点是一个非法节点
	{
		return FAIL;	// 返回失败,准备回溯
	}
	if (Length(pDataList) > bound)	//如果搜索深度达到了给定的深度
	{
		return FAIL;	// 返回失败,准备回溯
	}

	pRules = g_pRuleSet;	//获取所有规则
	
	PrintNode(pDataList);
	while (pRules)	//当规则集非空时
	{
		pRule = pRules;	//取规则集的第一个规则
		pRules = pRules->pNext;	//将第一个规则从规则集中删除
		pRData = Gen(pRule, pData);	//将第一个规则作用于当前节点,产生一个新节点
		if (pRData == NULL) return FAIL;	//空间不够,失败返回

		if (!Safe(pRData))	//如果新节点非法
		{
			free(pRData);	//释放掉该节点
			continue;	//试探下一个规则
		}
		
		
		pRDataList = ConsNode(pRData, pDataList);	//将新节点加入到从初始节点到当前的路径中
		pPath = Backtrack1(pRDataList, bound);	//新节点作为当前节点递归调用回溯算法
		

		if (pPath == FAIL)	//如果从当前节点到目标节点没有路径
		{

			free(pRData);	//释放当前节点
			continue;	//试探下一条规则
		}
		
		// 如果从当前节点到目标节点存在路径
		pRule = CopyRule(pRule);	//复制当前所用的规则
		if (pRule == NULL) return FAIL;	//空间不够,失败返回
		return ConsRule(pRule, pPath);	//将规则添加到规则表中,并作为解路径返回
	}
	return FAIL;	//如果规则表已空,则返回失败,回溯或者算法以失败结束
}

void main(void)
{
	struct NODE *pData = NULL;
	struct RULE *pPath = NULL;
	Input_MCK();
	g_pRuleSet = InitRules();	//初始化规则集
	
	pData = NewNode(M, C, 1);	//设置初始节点

	pPath = Backtrack1(pData, 20);	//调用回溯程序
	
	PrintRuleList(pPath);	//显示解路径
	
	if (pPath == FAIL)	//当求解失败时
	{
		free(pData);	//释放初始节点
	}
	FreeRuleList(pPath);	//释放解路径
	FreeRuleList(g_pRuleSet);	//释放规则集
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -