精华帖——八人过河的程序实现

题目:

一家六口,一个爸爸,一个妈妈,俩儿子,俩女儿,还有一个警察,一个坏蛋,过一条河。

爸爸不在妈妈伤害儿子,妈妈不在爸爸伤害女儿,警察不在坏蛋伤害一家六口。

只有妈妈爸爸警察会开船,一次只能过两个人,只有一艘船。

用程序实现怎么过河。

思路见程序:

include<iostream>
#include <vector>
#include <stdio.h>
using namespace std;
/*总体思路:用一个9位的整数表示,当前所处的状态,最高位表示河的左岸或者右岸。低8位依次表示警察,犯人,父亲
母亲,2个儿子,2个女儿;一次渡河可以看成是2个状态之间的转移。
总体分两步进行;
Step 1:先算出转移矩阵
Step 2:利用迪杰斯特拉算出,源点到终点最短路径,所经历的结点。
*/


#define MAX 10000//结点间的最大值,表示两个状态不能转换
#define N 512//表示状态向量,以9bit值表示。eg:0 1111 1111表示开始状态,所有的8个人在河的左岸。
const int lrmask=1<<8;//0表示在河的左岸,1表示在河的右岸。
const int cap= 1<<7;//表示警察的状态
const int criminal=1<<6;//表示犯人的状态
const int father=1<<5;//表示爸爸的状态
const int mother=1<<4;//表示妈妈的状态
const int son=3<<2;//表示两个儿子的状态
const int daughter=3;//表示两个女儿的状态



bool is_possible_state(int x)//判断某个状态是否满足规则
{
	if ((x&criminal)&&(!(x&cap))&&(x&(father+mother+son+daughter)))//表示警察不在的时候,犯人不能与6口家人在一起
		return false;
	if ((x&father)&&(!(x&mother))&&(x&daughter))//表示妈妈不在,爸爸不能与女儿在一起
		return false;
	if ((x&mother)&&(!(x&father))&&(x&son))//表示爸爸不在,妈妈不能与儿子在一起
		return false;
	else
		return true;//其它为有效状态
}

bool is_possible_vec(unsigned char x)//判断在船上的状态
{
	int num=0;
	int tmp=x;
	if (x==0)//表示状态向量的值为0 ,没人乘船,无效状态
		return false;
	while(tmp)
	{
		num++;
		if (num>2)//当多余2个人乘船,无效状态
			return false;
		tmp=tmp&(tmp-1);//判断几个人乘船
	}
	if ((x&criminal)&&(!(x&cap))&&(x&(father+mother+son+daughter)))//判断该状态是否满足规则
		return false;
	if ((x&father)&&(!(x&mother))&&(x&daughter))
		return false;
	if ((x&mother)&&(!(x&father))&&(x&son))
		return false;
	else
		return true;//满足情况,返回true.
}

 void Dijkstra(int v, int **dist,int D[N],int p[N],int s[N])   
 {     int i, j, k, v1, min, max=10000, pre;       
    v1=v;   
    for( i=0; i<N; i++)             
    {    D[i]=dist[v1][i];   
        if( D[i] != MAX )  p[i]= v1+1;   
        else p[i]=0;   
        s[i]=0;   
    }  
    s[v1]=1;                          
      for( i=0; i<N-1; i++)      
    {    min=10001;    
        for( j=0; j<N-1; j++)  
              if ( ( !s[j] )&&(D[j]<min) )           
                  {min=D[j];   
                        k=j;   
                     }  
                s[k]=1; 
    for(j=0; j<N; j++)  
     if ( (!s[j])&&(D[j]>D[k]+dist[k][j]) ) 
        {D[j]=D[k]+dist[k][j];   
        p[j]=k+1;                      
                }  
            }                               
            for(i=511; i<512; i++) //仅输出从源点状态0 1111 1111 到终点状态 1 0000 0000的路径
            {  
                printf(" %d : %d ", D[i], i);  
                pre=p[i];   
            while ((pre!=0)&&(pre!=v+1))  
            {    printf ("<- %d ", pre-1);   
                pre=p[pre-1];   
            }  
            printf("<-%d \n", v);   
        }  
} 

int main()
{
	int  **matrix=(int **)malloc(N*sizeof(int *));//创建二维状态矩阵matrix[N][N]
	for (int i=0;i<N;i++)
	{
		matrix[i]=(int *)malloc(N*sizeof(int));
	}
	
	//计算矩阵的值,即计算结点之间的路径。1表示可以连接,0表示自己与自己路径,MAX表示结点间不相连接
	for(int i=0;i<N;i++)
	{
		for (int j=0;j<N;j++)
		{
			if (i==j)
				matrix[i][j]=0;//自己与自己的距离为0
			else if(((i&lrmask)^(j&lrmask))&&is_possible_state(~j)&&is_possible_state(i)&&is_possible_state(~j)&&is_possible_state(j))
			{
			 //当满足i,j分别为某次转移前左岸的状态与转移后右岸的状态;
			 //~i和~j分别表示某次转移前右岸的状态与转移后左岸的状态
				unsigned char i0=i%256;//i0,j0分别表示状态向量值的后八位,即八个人在左岸与右岸的状态
				unsigned char j0=j%256;
				unsigned char tmp=~i0^j0;//tmp表示转移向量值,即某次那些人状态发生了改变
				
				//在满足上述情况的基础上,tmp转移向量值有效
				if(((unsigned char)i0>(unsigned char)(~j0))&&is_possible_vec(tmp)&&!(tmp-(tmp&i0)))
					matrix[i][j]=1;
				else
					matrix[i][j]=MAX;//表示节点间不相连

			}
			else
				matrix[i][j]=MAX;//表示节点间不相连
		}
	}
	int D[N]={0};//数组D表示经过了几个结点  
    int p[N]={0};//数组P存储的是从源点到终点经过了那些结点  
    int s[N]={0};//保存已经找到的结点  
    int num=0; 
	Dijkstra(255,matrix,D,p,s);//运用迪杰斯特拉算法,初始状态从0 1111 1111开始
}
题外话:这是面试某狗测试岗的课外作业。哈哈~~

霸面某狗,遇到一个很nice的面试官,问的问题虽然回答的不是特别好,但是却让我大开眼界,原来类似智力题这么好玩!!!哈哈~~

下午3点结束面试,拿到的题目——之前听过没做过的题目。来不及马上做,又去赶另一家互联网的面试。唉,一波面试向我袭来,有些措手不及。

晚上回去搜一下,原来是个小游戏。哈哈,从没想过自己能写出个游戏。

整个思考过程表示很痛苦,痛并快乐着。整个晚上很high,熬到夜里3点多。。。竟然不感觉困。。。

因为刚开始思路有点绕路了,耽误些时间,提交时间晚了2个多小时。所以,这可能也是没通过的原因之一。

不过,找到了许久以来没有的学习兴趣与热情。。。。哈哈,很nice!!!


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值