/*
*功能:解决狼羊人过河问题
*作者:王文堃
*作者邮箱:wenkun_wang@163.com
*创建时间:2016/4/5
*/
/*
问题描述:有一个人带着一匹狼、一头羊和白菜要过河
已知人每次过河只能带一样东西,狼和羊不能单独在一起
羊和菜不能单独在一起,求人过河的方案有几种?
问题抽象:分别用m、w、g、c来表示人(men)、狼(wolf)、羊(goat)、菜(cabbage)
问题的解决步骤:
1.求出人狼羊菜在河两岸的所有可能性,排列组合共16种情况,分别是
c40:(mwgc|)
c41:(wgc|m)、(mgc|w)、(mwc|g)、(mwg|c)
c42:(gc|mw)(wc|mg)(wg|mc)(mc|wg)(mg|wc)(mw|gc)
c43:(m|wgc)(w|mgc)(g|mwc)(c|mwg)
c44:(|mwgc)
----------------------------------------------------------
2.根据条件去除不可能的状态,将狼羊、羊菜单独在河一边的情况去除,
合适情况10种分别是:
(mwgc|)
(mgc|w)、(mwc|g)、(mwg|c)
(wc|mg)、(mg|wc)
(w|mgc)、(g|mwc)、(c|mwg)
(|mwgc)
----------------------------------------------------------
3.确定邻接关系,形成一个图,使用邻接矩阵存储【也可使用邻接链表】
----------------------------------------------------------
4.对图进行遍历,从(mwgc|)出发,用深度优先搜索找到(|mwgc)则为一个可行的方案
【也可使用广度优先搜索】
*/
#include<iostream>
#include<fstream>
#include<malloc.h>
using namespace std;
#define datatype char
#define ALLSTATE 1
#define SELECTSTATE 2
#define MAXSIZE 10
#define TOLEFT 0
#define TORIGHT 1
//数据结构说明
//记录所有的情况
//使用链表是为了后面将错误的状态删除比较方便
typedef struct node
{
int num; //用来标识情况的序号
datatype state[10]; //用来说明情况
bool IsRight; //用来指示该情况是否符合条件
struct node *next; //指向下一个元素
}MyState;
typedef struct
{
char vertex[MAXSIZE][MAXSIZE]; //顶点
int StateGraph[MAXSIZE][MAXSIZE]; //邻接矩阵
}MyGraph;
//全局变量声明
int num_situation = 0; //统计情况的个数
MyState *AllState = (MyState*)malloc(sizeof(MyState)); //创建一个头指针
MyState *pEnd = AllState; //用来指示链尾部
datatype way[] = { 'm','w','g','c' }; //操作的数组
ofstream out("situation.txt"); //用于将所有可能写入文件
ifstream in("situation.txt"); //用于从文件中读取所有可能
MyGraph *g = (MyGraph*)malloc(sizeof(MyGraph)); //建立图的指针
int visited[MAXSIZE]; //用来记录图中一个结点是否被访问过,值为访问它的结点编码
//函数声明
//-------------------1------------------------------------------------
void count_situation(); //计算所有可能的情况
void comb(datatype a[], int n, int m, datatype b[], const int M); //组合
void file2struct(); //将情况补充后存入结构体
void fix(char state[]); //补充情况
int output_allstate(int sel); //输出所有情况
//-------------------2------------------------------------------------
void select_situation(); //筛选情况
bool IsRight(char temp[]); //判断该状态是否正确
//-------------------3------------------------------------------------
void create_graph(); //建立邻接矩阵
void init_Graph();
void GetNextState(MyState* NowState, int Edge[]); //根据当前状态求后继状态
void PassTheBrige(char temp[], char ch, int derction); //ch过河
int GetStateNum(char state[]); //获得此状态的编号
bool IsEqual(char array1[], char array2[]);
void output_graph(); //输出图
//-------------------4------------------------------------------------
void FindTheAnswer(int start, int end); //找到对应方法
void DFS(int s, int e); //找路径
void printPath(int e); //输出路径
char* Num2State(int num); //根据编号返回对应状态
void main()
{
count_situation(); //第一步:计算所有状态
select_situation(); //第二步:筛选合法状态
create_graph(); //第三步:建立邻接矩阵
FindTheAnswer(GetStateNum("|mwgc"),GetStateNum("wmgc|")); //第四步:深度优先搜索
getchar();
}
/*------------------------------第一步计算所有情况--------------------------------------*/
//计算所有可能的情况
void count_situation()
{
datatype b[4];
out << "|mwgc" << endl;
for (int i = 1; i <= 4; i++)
{
comb(way, 4, i, b, i); //组合
}
out.close();
file2struct(); //将文件中的情况修正后存入结构体
//输出全部结果,并修改状态个数
num_situation = output_allstate(ALLSTATE);
}
//组合
void comb(datatype a[], int n, int m, datatype b[], const int M)
{
for (int i = n; i >= m; i--)
{
b[m - 1] = i - 1;
if (m > 1)
{
comb(a, i - 1, m - 1, b, M);
}
else //m==1
{
for (int j = M - 1; j >= 0; j--)
{
out << a[b[j]]; //写到文件
}
out << "|" << endl;
}
}
}
//将情况补充后存入结构体
void file2struct()
{
int number = 0;
char state[10];
if (in)
{
while (!in.eof())
{
//读取一条状态
in >> state;
//补充状态
fix(state);
//将state保存到结构体中
MyState *temp = (MyState*)malloc(sizeof(MyState));
temp->num = number++;
temp->next = NULL;
strcpy(temp->state, state); //将补充过的状态填入结构体
pEnd->next = temp;
pEnd = temp;
}
}
}
//补充情况
void fix(char state[])
{
int i = 0;
bool m = false, w = false, g = false, c = false;
//检查传进来的串种缺少什么元素
while (state[i] != '|')
{
switch (state[i++])
{
case 'm':
m = true;
break;
case 'w':
w = true;
break;
case 'g':
g = true;
break;
case 'c':
c = true;
break;
default:
break;
}
}
//将所有缺少的元素补充到‘|’后面
if (m == false)
state[++i] = 'm';
if (w == false)
state[++i] = 'w';
if (g == false)
state[++i] = 'g';
if (c == false)
state[++i] = 'c';
}
/*------------------------------第二步判断情况正误--------------------------------------*/
//筛选情况
void select_situation()
{
MyState *temp = AllState;
int i = -1;
while (temp->next != NULL)
{
if (!IsRight(temp->next->state)) //如果该状态不正确
{
//将改状态删除
MyState *del = temp->next;
temp->next = del->next;
free(del);
}
else
{
temp->num = i++;
temp = temp->next;
}
}
//输出筛选过的结果,并更新状态个数
num_situation = output_allstate(SELECTSTATE);
}
//判断该状态是否正确
bool IsRight(char temp[])
{
int i = 0;
bool m = false, w = false, g = false, c = false;
while (temp[i] != '|' && temp[i] != '\0')
{
switch (temp[i++])
{
case 'm':
m = true;
break;
case 'w':
w = true;
break;
case 'g':
g = true;
break;
case 'c':
c = true;
break;
default:
break;
}
}
//wgc|m
if (w == true && g == true && c == true && m == false)
return false;
//m|wgc
if (m == true && w == false && g == false && c == false)
return false;
//gc|mw
if (g == true && c == true && m == false && w == false)
return false;
//mw|gc
if (m == true && w == true && g == false && c == false)
return false;
//wg|mc
if (w == true && g == true && m == false && c == false)
return false;
//mc|wg
if (m == true && c == true && w == false && g == false)
return false;
return true;
}
//输出函数
int output_allstate(int sel)
{
int i = 1;
MyState *temp = AllState->next;
if (sel == ALLSTATE)
cout << "-------------所有的情况----------------" << endl;
else if (sel == SELECTSTATE)
cout << "-------------筛选后情况----------------" << endl;
while (temp->next != NULL)
{
cout << "第" << i++ << "种情况:" << temp->state << endl;
temp = temp->next;
}
return i - 1; //返回当前状态的个数
}
/*------------------------------第三步建立邻接链表--------------------------------------*/
//建立邻接矩阵
/*
编程思路:
1.根据状态的个数建造一个邻接矩阵
2.当状态下一个指针不空的时候,对每一个状态有如下3~6的操作
3.求得改状态之后的所有可能状态
4.对所有的可能状态进行判断排除可能状态中不满足条件的
5.将满足条件的下一个状态与当前状态连边
6.下一个状态为当前状态
当状态下一个指向为空时图生成完毕
*/
void create_graph()
{
init_Graph();
int Edge[4] = { -1,-1,-1,-1 }; //记录应该链接的边
MyState *temp = AllState->next;
int i = 0;
//遍历状态链表
while (temp->next != NULL)
{
//顶点操作
//给各个顶点赋初值
strcpy(g->vertex[i++], temp->state);
//边操作
//求当前状态每一种满足条件的后继状态
GetNextState(temp,Edge);
//对每一个后记状态进行边添加
for (int j = 0; j < 4; j++)
{
if (Edge[j] != -1) //要加边
{
g->StateGraph[temp->num][Edge[j]] = 111;
}
}
//恢复Edge
for (int j = 0; j < 4; j++)
{
Edge[j] = -1;
}
temp = temp->next;
}
//输出图的邻接矩阵
output_graph();
}
//初始化图
void init_Graph()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
g->StateGraph[i][j] = 0;
}
}
}
//根据当前状态求后继状态
void GetNextState(MyState* NowState, int Edge[])
{
//判断当前状态中人的位置
int i_Edge = 0;
int i = 0;
bool m = false, w = false, g = false, c = false;
//保存NowState
char Copy[10];
strcpy(Copy, NowState->state);
while (NowState->state[i] != '|' && NowState->state[i] != '\0')
{
switch (NowState->state[i++])
{
case 'm':
m = true;
break;
case 'w':
w = true;
break;
case 'g':
g = true;
break;
case 'c':
c = true;
break;
default:
break;
}
}
//人在河左
if (m == true)
{
//狼在河左,带狼过
if (w == true)
{
PassTheBrige(Copy, 'w', TORIGHT);
PassTheBrige(Copy, 'm', TORIGHT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//羊在河左,带羊过
if (g == true)
{
PassTheBrige(Copy, 'g', TORIGHT);
PassTheBrige(Copy, 'm', TORIGHT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//菜在河左,带菜过
if (c == true)
{
PassTheBrige(Copy, 'c', TORIGHT);
PassTheBrige(Copy, 'm', TORIGHT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//人自己过河
PassTheBrige(Copy, 'm', TORIGHT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
else //人在河右
{
//狼在河右,带狼过
if (w == false)
{
PassTheBrige(Copy, 'w', TOLEFT);
PassTheBrige(Copy, 'm', TOLEFT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//羊在河左,带羊过
if (g == false)
{
PassTheBrige(Copy, 'g', TOLEFT);
PassTheBrige(Copy, 'm', TOLEFT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//菜在河左,带菜过
if (c == false)
{
PassTheBrige(Copy, 'c', TOLEFT);
PassTheBrige(Copy, 'm', TOLEFT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
//人自己过河
PassTheBrige(Copy, 'm', TOLEFT);
if (IsRight(Copy))
{
Edge[i_Edge++] = GetStateNum(Copy);
}
strcpy(Copy, NowState->state);
}
}
//过河函数,将temp数组中的字母ch移动到‘|’对岸
void PassTheBrige(char temp[], char ch, int derction)
{
int i = 0, j = 0;
//找ch的位置
for (i = 0; i < 5; i++)
{
if (temp[i] == ch)
break;
}
//将ch删除
for (j = i; j < 5; j++)
{
temp[j] = temp[j + 1];
}
//找河的位置
for (i = 0; i < 5; i++)
{
if (temp[i] == '|')
break;
}
if (derction == TORIGHT)
{
temp[4] = ch; //给最后一个
}
else //过到河左
{
for (j = 4; j >= i; j--)
{
temp[j + 1] = temp[j];
}
temp[i] = ch;
}
}
//获得此状态的num编号
int GetStateNum(char state[])
{
MyState* temp = AllState->next;
while (temp->next != NULL)
{
if (IsEqual(temp->state,state))
{
return temp->num;
}
else
{
temp = temp->next;
}
}
return -1;
}
//判断两个状态是否相等
bool IsEqual(char array1[], char array2[])
{
int i = 0;
bool m1 = false, w1 = false, g1 = false, c1 = false;
bool m2 = false, w2 = false, g2 = false, c2 = false;
while (array1[i] != '|' && array1[i] != '\0')
{
switch (array1[i++])
{
case 'm':
m1 = true;
break;
case 'w':
w1 = true;
break;
case 'g':
g1 = true;
break;
case 'c':
c1 = true;
break;
default:
break;
}
}
i = 0;
while (array2[i] != '|' && array2[i] != '\0')
{
switch (array2[i++])
{
case 'm':
m2 = true;
break;
case 'w':
w2 = true;
break;
case 'g':
g2 = true;
break;
case 'c':
c2 = true;
break;
default:
break;
}
}
if (m1 == m2 && w1 == w2 && g1 == g2 && c1 == c2)
return true;
else
return false;
}
//输出图
void output_graph()
{
int i = 0, j = 0;
cout << endl;
cout << "-------------邻接矩阵为----------------" << endl;
cout << " ";
for (i = 0; i < 10; i++)
{
cout << g->vertex[i] << " ";
}
cout << endl;
for (i = 0; i < 10; i++)
{
cout << g->vertex[i] << " ";
for (j = 0; j < 10; j++)
{
cout << g->StateGraph[i][j]<<" ";
}
cout << endl;
cout << endl;
}
cout << endl;
}
/*------------------------------第四步进行深度搜索--------------------------------------*/
//找路径
void FindTheAnswer(int start, int end)
{
//初始化
for (int i = 0; i < MAXSIZE; i++)
{
visited[i] = -1;
}
visited[start] = start;
cout << endl;
cout << "-------------最终结果为----------------" << endl;
DFS(start, end);
}
//深度优先搜索
void DFS(int s, int e)
{
if (s == e)
{
printPath(e);
cout << endl;
}
for (int i = 1; i < MAXSIZE; i++)
{
if (g->StateGraph[s][i] > 0 && visited[i] == -1) //有边且没有被访问
{
visited[i] = s; //标志边被s点访问
DFS(i, e);
visited[i] = -1; //回溯后修改边为未被访问
}
}
}
//输出
void printPath(int e)
{
if (visited[e] != e)
{
printPath(visited[e]);
cout << "-->";
}
//输出e所对应的状态
cout << " (" << Num2State(e) << ") ";
}
//根据编号返回对应状态
char* Num2State(int num)
{
MyState* temp = AllState->next;
while (temp->next != NULL)
{
if (temp->num == num)
{
return temp->state;
}
else
{
temp = temp->next;
}
}
return NULL;
}
最终的结果为: