数据结构——中国邮递员问题

/*
6 7
1 2 2
1 3 1
1 4 6
2 4 2
3 4 4
4 5 3
5 6 5
*/

在别人的内容上加以修改后


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)

#define min(a,b) ( (a) < (b) ? (a) : (b) )
#define MAX_NODE 100
#define MAX_EDGE 100
#define INF 0x7fffffff      // 表示两点不连通

typedef struct
{
    int number;   // 标记位
    int cost;     // 结点间距
    int dis;      // 结点最近距离
}Node;

typedef struct
{
    int from;     // 边起点
    int to;       // 边终点
    int dis;      // 边距离
}Edge;

int n, m;                              // 点的个数和边的个数
int total_edge, odd_point;             // 总边数和奇点数
Node map[MAX_NODE][MAX_NODE];          // 结点连接情况
int point[MAX_NODE];                   // 每个结点的度数
int need_add_num, min_count, edge_num; // 需要添加边的个数  
Edge odd_edge[MAX_EDGE];
int point_flag[MAX_EDGE];
int min_edge[MAX_EDGE];
int tmp_edge[MAX_EDGE];
int top;
int finish_flag = 0;
int path_stack[MAX_EDGE];

void Floyd()   任意两点之间的最短距离
{
    // 比较i到j直达近还是从i到k加上k到j近。添加的结点k放在最外层循环。
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            if (map[i][k].dis != INF)
            {
                for (int j = 1; j < n; j++)
                    if (map[k][j].dis != INF)
                    {
                        map[i][j].dis = map[j][i].dis = min(map[i][j].dis, map[i][k].dis + map[k][j].dis);
                    }
            }
}

void search_edge(int count, int step)
将这些奇度点进行匹配,找到需要“补边”的和为最小的奇度点对们;返回的值为min_edge[i] 这里面存的是odd_edge中的信息
{
    // 遍历寻找最优方案
    // step用于记录数量,count记录最短长度
    // 寻找路径存入了数组中,可通过i访问
    int a, b, c;
    for (int i = 0; i < edge_num; i++)
    {

        if (step == need_add_num) 奇度点数目/2
        {
            if (count < min_count)
            {
                for (int i = 0; i < need_add_num; i++)
                {
                    min_edge[i] = tmp_edge[i]; 
                     这里存放的应该是两个虚边相加后值最小的 两个虚边 ;存的值为虚边的下标
                }
                min_count = count;
            }
            int step = 0;
            int count = 0;
            break;
        }

        a = odd_edge[i].from;
        b = odd_edge[i].to;
        c = odd_edge[i].dis;
        if (point_flag[a] == 1 && point_flag[b] == 1)
            // 如果两点均未被相连
        {
            point_flag[a] = point_flag[b] = 0;    // 置标记位为0
            tmp_edge[step] = i;
            search_edge(count + c, step + 1);
            point_flag[a] = point_flag[b] = 1;
        }
    }
}

void dijkstra_to_add_edge(int s, int e) 
开始之前: 已经找到两两匹配的奇度点(通过search_edge已经找到 x对 和为最小的 奇度点对)   
要做的工作:找到两点之间的最短距离(从这里找到经过的路径(两个奇度点相连经过的点),通过前驱节点;然后将每条边都加上)
{
    int dis[MAX_NODE];       // 点到起始(s)点的距离
    int used[MAX_NODE];      // 标记位
    int pre[MAX_NODE];       // 记录其从哪一点过来的,方便回溯

    memset(used, 0, sizeof(used));   // 初始化一波
    for (int i = 1; i <= n; i++)
    {
        dis[i] = INF;
    }
    dis[s] = 0;

    while (1)
    {
        int v = -1;
        for (int i = 1; i <= n; i++)
        {
            if (!used[i] && (v == -1 || dis[i] < dis[v]))
            {
                v = i;
            }
        }
        if (v == e || v == -1)
            // 当当前点是末点e时或本身v就是最小时
            break;
        used[v] = 1;    // 修改标记位
        for (int i = 1; i <= n; i++)
        {
            if (map[v][i].cost < INF && (dis[v] + map[v][i].cost) < dis[i])
            {
                pre[i] = v;
                dis[i] = dis[v] + map[v][i].cost;
            }
        }
    }
    int v = e;
    int pre_node = e;
    while (pre_node != s)
    {
        pre_node = pre[v];       
        ++map[pre_node][v].number;    // 加边
        ++map[v][pre_node].number;   number的值用来标记两点之间有几条边
        total_edge++; 
        printf("添加的边为:%c%c\n",pre_node + 'A' - 1,v + 'A' - 1);
        v = pre[v];       节点v的前驱节点
    }
}

void extend_edge(int add_num)
{
    need_add_num = add_num;
    memset(point_flag, 0, sizeof(point_flag));
    edge_num = 0;
    // 当两个点都是奇点的时候
    for (int i = 1; i < n; i++)
    {
        if ((point[i] & 0x1) == 1)
        {
            for (int j = i + 1; j <= n; j++)
            {
                if ((point[j] & 0x1) == 1 && map[i][j].dis < INF)
                {
                    point_flag[i] = point_flag[j] = 1;   // 将i,j两点标记为需要被连接的奇点
                    odd_edge[edge_num].from = i;         // 将相关信息存入odd_edge数组中
                    odd_edge[edge_num].to = j;
                    odd_edge[edge_num].dis = map[i][j].dis;
                    edge_num++;
                }
            }
        }
    }
    min_count = INF;   // 设置最小值,方便比较
    search_edge(0, 0);
    if (min_count < INF)
    {
        int a, b;
        for (int i = 0; i < need_add_num; i++)
        {
            int k = min_edge[i];
            a = odd_edge[k].from;
            b = odd_edge[k].to;
            dijkstra_to_add_edge(a, b);   // 用dijkstra算法求加边后两点最短距离
            point[a] = point[b] = 0; 这个是每个点的度数,不应该是添加了边的点的度数++吗????
        }
        odd_point -= add_num * 2;
    }
    else
    {
        exit(-1);
    }

}

void search_euler_path(int s)
{
    for (int i = 1; i <= n; i++)
    {
        if (map[s][i].number > 0)
        {
            --map[s][i].number;
            --map[i][s].number;
            path_stack[top++] = i;
            if (top == (total_edge + 1))
                // 结点比总边数多1
            {
                finish_flag = 1;
                return;
            }

            search_euler_path(i);
            if (finish_flag)
                return;
            ++map[s][i].number;
            ++map[i][s].number;
            --top;
        }
    }
}


int main()
{
    printf("\n请输入顶点数和边数:");
    while (scanf("%d %d", &n, &m) != EOF)
    {
        // 初始化
        memset(point, 0, sizeof(point));
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                map[i][j].cost = map[i][j].dis = INF;
        total_edge = 0;

        // 读入图的数据
        int a, b, c;
        printf("请输入每条边的起点,终点和权值:\n");
        for (int i = 0; i < m; i++)
        {
            scanf("%d %d %d", &a, &b, &c);
            map[a][b].cost = map[b][a].cost = c;
            map[a][b].dis = map[b][a].dis = c;
            map[a][b].number = map[b][a].number = 1;
            ++point[a];
            ++point[b];
            ++total_edge;
        }

        odd_point = 0;
        for (int i = 1; i <= n; i++)
            // 判断点是否为奇点
        {
            if ((point[i] & 0x1) == 1)
            {
                odd_point++;
            }
        }

        int first_id = 1;    // 设置邮局的位置 1即为A
        if (odd_point >= 2) 当奇点的数目大于等于2时
            // 用floyd算法更新任意两点之间最短路径,并且添加边
        {
            Floyd();
            extend_edge(odd_point / 2);
            // 两个奇点添加“一条边”,共需要添加odd_point/2条边
        }

        top = 0;
        path_stack[top++] = first_id;
        // 利用栈进行深度搜索来确定最短路线并存入数组
        search_euler_path(first_id);

        printf("总边数为:%d\n", total_edge);
        char vex;
        // 将最短路线输出
        for (int i = 0; i <= total_edge; i++)
        {
            vex = path_stack[i] + 'A' - 1;
            printf("%c", vex);
            if (i < total_edge)
            {
                printf("->");
            }
        }
        printf("\n");
    }

    return 0;
}
 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值