求无向图中满足约束条件的路径

/**
*    实验题目:
*        求无向图中满足约束条件的路径
*    实验目的:
*        掌握深度优先遍历算法在求解图路径搜索问题中的应用。
*    实验内容:
*        编写一个程序,设计相关算法,从如图8.24所示的无向图G中找出满足如下
*    条件的所有路径:
*    1、给定起点u和终点v。
*    2、给定一组必经点,即输出的路径必须包含这些顶点。
*    3、给定一组必避点,即输出的路径不能包含这些顶点。
*    采用全局变量V1[0..n-1]表示必经点,V2[0..m-1]表示必避点。
*/

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>

#define INF     32767               //定义∞
#define MAXV    100                 //最大顶点个数

typedef char InfoType;
/*-------------------------以下定义邻接矩阵类型---------------------------*/
typedef struct
{
    int no;                         //顶点编号
    InfoType info;                  //顶点信息
}VertexType;                        //顶点类型

typedef struct
{
    int edges[MAXV][MAXV];          //邻接矩阵数组(用一个二维数组存放顶点间关系(边或弧)的数据)
    int n;                          //顶点数
    int e;                          //边数
    VertexType vexs[MAXV];          //存放顶点信息(用一个一维数组存放图中所有顶点数据)
}MatGraph;                          //完整的图邻接矩阵类型

//邻接表表示法-将每个顶点的邻接点串成一个单链表
/*-----------以下定义邻接表类型--------------*/
typedef struct ArcNode
{
    int adjvex;                     //该边的邻接点编号
    struct ArcNode *nextarc;        //指向下一条边的指针
    int weight;                     //该边的相关信息,如权值(用整型表示)
}ArcNode;                           //边结点类型

typedef struct VNode
{
    InfoType info;                  //顶点其他信息
    int cnt;                        //存放顶点入度,仅用于拓扑排序
    ArcNode *firstarc;              //指向第一条边
}VNode;                             //邻接表结点类型

typedef struct
{
    VNode adjlist[MAXV];            //邻接表头结点数组
    int n;                          //图中顶点数
    int e;                          //图中边数
}AdjGraph;                          //完整的图邻接表类型

/*-------------------------邻接矩阵的基本运算算法---------------------------*/
/*------------由边数组A、顶点数n和边数e创建图的邻接矩阵g--------------------*/
void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e)
{
    int i, j;

    g.n = n;
    g.e = e;
    for(i = 0; i < g.n; i++)
        for(j = 0; j < g.n; j++)
            g.edges[i][j] = A[i][j];
}

/*------------输出邻接矩阵g--------------------*/
void DispMat(MatGraph g)
{
    int i, j;

    for(i = 0; i < g.n; i++)
    {
        for(j = 0; j < g.n; j++)
        {
            if(g.edges[i][j] != INF)
                printf("%4d", g.edges[i][j]);
            else
                printf("%4s", "∞");
        }
        printf("\n");
    }
}

/*-------------------------邻接表的基本运算算法---------------------------*/
/*-------------------由边数组A、顶点数n和边数e创建图的邻接表G--------------------*/
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
    int i, j;
    ArcNode *p;

    G = (AdjGraph *)malloc(sizeof(AdjGraph));
    for(i = 0; i < n; i++)                              //给邻接表中所有头结点的指针域置初值NULL
    {
        G->adjlist[i].firstarc = NULL;
    }

    for(i = 0; i < n; i++)                              //检查邻接矩阵中的每个元素
    {
        for(j = n - 1; j >= 0; j--)
        {
            if(A[i][j] != 0 && A[i][j] != INF)          //存在一条边
            {
                p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
                p->adjvex = j;                          //邻接点编号
                p->weight = A[i][j];                    //边的权重
                p->nextarc = G->adjlist[i].firstarc;    //采用头插法插入结点p
                G->adjlist[i].firstarc = p;
            }
        }
    }
    G->n = n;
    G->e = e;
}

/*-------------------输出邻接表G--------------------*/
void DispAdj(AdjGraph *G)
{
    ArcNode *p;

    for(int i = 0; i < G->n; i++)
    {
        p = G->adjlist[i].firstarc;
        printf("顶点%d: ", i);
        while(p != NULL)
        {
            printf("%3d[%d]->", p->adjvex, p->weight);  //邻接点编号[权重]
            p = p->nextarc;
        }
        printf("∧\n");
    }
}

/*-------------------销毁图的邻接表G--------------------*/
void DestroyAdj(AdjGraph *&G)
{
    ArcNode *pre, *p;

    for(int i = 0; i < G->n; i++)
    {
        pre = G->adjlist[i].firstarc;                   //pre指向第i个单链表的首结点
        if(pre != NULL)
        {
            p = pre->nextarc;
            while(p != NULL)                            //释放第i个单链表的所有边结点
            {
                free(pre);
                pre = p;
                p = p->nextarc;
            }
            free(pre);
        }
    }
    free(G);                                            //释放头结点数组
}

/*--------------全局变量-----------------*/
int visited[MAXV];
int n;                          //必经点个数
int m;                          //必避点个数
int V1[MAXV];                   //必经点集合
int V2[MAXV];                   //必避点集合
int cnt = 0;

/*----------------判断条件----------------------*/
static bool cond(int path[], int d)
{
    int flag1 = 0, f1, flag2 = 0, f2, i, j;

    //判断路径中是否有必经点
    for(i = 0; i < n; i++)
    {
        f1 = 1;
        for(j = 0; j <= d; j++)
        {
            if(path[j] == V1[i])
            {
                f1 = 0;
                break;
            }
        }
        flag1 += f1;
    }

    //判断路径中是否有必避点
    for(i = 0; i < m; i++)
    {
        f2 = 0;
        for(j = 0; j <= d; j++)
        {
            if(path[j] == V2[i])
            {
                f2 = 1;
                break;
            }
        }
        flag2 += f2;
    }

    if(flag1 == 0 && flag2 == 0)    //满足条件返回true
        return true;
    else                            //不满足条件返回false
        return false;
}

/*----------------在图G中查找从顶点vi到顶点vj的满足条件的路径------------------*/
static void trav_path(AdjGraph *G, int vi, int vj, int path[], int d)
{
    int v, i;
    ArcNode *p;

    visited[vi] = 1;
    d++;
    path[d] = vi;
    if(vi == vj && cond(path, d))
    {
        printf("  路径%d: ", ++cnt);
        for(i = 0; i < d; i++)
            printf("%d->", path[i]);
        printf("%d\n", path[i]);
    }
    p = G->adjlist[vi].firstarc;            //找vi的第一个邻接顶点
    while(p != NULL)
    {
        v = p->adjvex;                      //v为vi的邻接顶点
        if(visited[v] == 0)                 //若该顶点未标记访问,则递归访问之
            trav_path(G, v, vj, path, d);
        p = p->nextarc;                     //找vi的下一个邻接顶点
    }
    visited[vi] = 0;                        //取消访问标记,以使该顶点可重新使用
    d--;
}

int main(void)
{
    int u;                      //起点
    int v;                      //终点
    int i;                      //循环变量
    int path[MAXV];
    AdjGraph *G;
    int A[MAXV][MAXV] = {
        {0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
        {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
        {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
        {0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0},
        {0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
    };
    CreateAdj(G, A, 15, 21);   //建立邻接表
    printf("图G的邻接表:\n");
    DispAdj(G);

    for(i = 0; i < MAXV; i++)
        visited[i] = 0;
    printf("输入起点和终点:");
    scanf("%d%d", &u, &v);
    printf("输入必经点个数:");
    scanf("%d", &n);
    printf("输入必经点集合:");
    for(i = 0; i < n; i++)
        scanf("%d", &V1[i]);
    printf("输入必避点个数:");
    scanf("%d", &m);
    printf("输入必避点集合:");
    for(i = 0; i < n; i++)
        scanf("%d", &V2[i]);
    printf("\n所有的探宝路径如下:\n");
    trav_path(G, u, v, path, -1);
    DestroyAdj(G);

    return 0;
}
测试结果:

图G的邻接表:
顶点0:   1[1]->  2[1]->  3[1]->  4[1]->∧
顶点1:   0[1]->  6[1]->  8[1]->∧
顶点2:   0[1]->∧
顶点3:   0[1]->  6[1]->∧
顶点4:   0[1]->  7[1]->∧
顶点5:   8[1]->  9[1]->∧
顶点6:   1[1]->  3[1]->  7[1]->  9[1]->∧
顶点7:   4[1]->  6[1]-> 10[1]-> 11[1]->∧
顶点8:   1[1]->  5[1]-> 12[1]->∧
顶点9:   5[1]->  6[1]-> 10[1]-> 12[1]->∧
顶点10:   7[1]->  9[1]-> 13[1]->∧
顶点11:   7[1]-> 13[1]->∧
顶点12:   8[1]->  9[1]-> 14[1]->∧
顶点13:  10[1]-> 11[1]-> 14[1]->∧
顶点14:  12[1]-> 13[1]->∧
输入起点和终点:0 14
输入必经点个数:2
输入必经点集合:6 10
输入必避点个数:2
输入必避点集合:1 5

所有的探宝路径如下:
  路径1: 0->3->6->7->10->9->12->14
  路径2: 0->3->6->7->10->13->14
  路径3: 0->3->6->7->11->13->10->9->12->14
  路径4: 0->3->6->9->10->7->11->13->14
  路径5: 0->3->6->9->10->13->14
  路径6: 0->4->7->6->9->10->13->14

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值