八人过河 程序 C++

这里写图片描述

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
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);   //从右往左,左岸-》右岸交替,最后13步 
        }  
} 
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开始
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值