分支限界法解旅行商问题

问题描述

某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),要选定一条从驻地触发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。

算法分析

旅行售货员问题的解空间可以组织成一棵树,从树的根结点到任一叶结点的路径定义了图的一条周游路线。旅行售货员问题要在图G中找出费用最小的周游路线。路线是一个带权图。图中各边的费用(权)为正数。图的一条周游路线是包括V中的每个顶点在内的一条回路。周游路线的费用是这条路线上所有边的费用之和。

/* 
优先队列的操作MinH返回优先队列H中就有最小优先级的元素 
    InsertxH将x插入优先队列中 
    DeleteMinH删除并返回H中具有最小优先级的元素 
*/ 
# include <stdio.h> 
# include <stdlib.h> 
# define HeapSize 1000 
# define Max 9999 
typedef struct{ 
     int i; 
     int node; 
     int length; 
}Node; 
typedef struct { 
     int n; 
     int arcnum; 
     Node * nod; 
     int * prev; 
     int ** c; 
     int * dist; 
}Graph; 
typedef struct minheap { 
     int last; 
     int maxsize; 
     Node * heep; 
}Minheap, * Heap; 
void MinHeapInit (Heap &H) { 
     H = (Heap) malloc (sizeof(Minheap)); 
     H->maxsize = HeapSize; 
     H->last = 0; 
     H->heep = (Node *) malloc ((H->maxsize + 1) * sizeof(Node)); 
} 
void HeapInsert (Node x, Heap H) { 
     int i; 
     if (H->last == H->maxsize){ 
         printf("堆已满\n"); 
         exit(0); 
     } 
     i = ++ H->last; 
     while (i != 1 && x.node < H->heep[i/2].node){ 
         H->heep[i] = H->heep[i/2]; 
         i /= 2; 
     } 
     H->heep[i] = x; 
} 
void DeleteMin (Heap H, Node * x) { 
    int i, ci; 
    Node y; 

    if (H->last == 0){
        printf("空堆\n"); 
        exit(0); 
    } 
    *x = H->heep[1]; 
    y = H->heep[H->last --]; 
    i = 1; 
    ci = 2; 
    while (ci <= H->last){ 
        if (ci < H->last && (H->heep[ci + 1].node < H->heep[ci].node)) 
            ci ++; 
        if (H->heep[ci].node > y.node) 
            break; 
        H->heep[i] = H->heep[ci]; 
        i = ci; 
        ci *= 2; 
    } 
    H->heep[i] = y; 
} 
void InitGraph(Graph &G) { 
    int i, j, k ,x; 

    printf("请输入图中的弧的个数"); 
    scanf("%d",&G.arcnum); 
    k = G.arcnum; 
    for (i = 0; i < G.n; i++) { 
        G.nod[i].i = i; 
        printf("请输入第 %d 个节点的内容", i); 
        scanf("%d",&G.nod[i].node); 
        G.nod[i].length = 0; 
    } 
    for (i = 0; i < G.n; i++){ 
        G.dist[i] = Max; 
        G.prev[i] = -1; 
        for (j = 0; j < G.n; j++) 
            G.c[i][j] = Max; 
    } 
    while (k){
        printf("请输入第%d条边的开始节点、结束节点下标以及边的权值\n", k); 
        scanf("%d %d %d",&i, &j, &x); 
        G.c[i][j] = x; 
        k --; 
    } 
} 
void ShortestPaths(Graph &G, int v){ 
    Heap H; 
    Node E = G.nod[v]; 
    Node N; 
    G.dist[v] = 0; 
    MinHeapInit(H); 
    HeapInsert(E,H); 
    while (1){ 
        for (int j = 0; j < G.n; j++){ 
            if ((G.c[E.i][j] < Max) && (G.c[E.i][j] + E.length < G.dist[j])){ 
                G.dist[j] = E.length + G.c[E.i][j]; 
                G.prev[j] = E.i; 
                N.i = j; 
                N.length = G.dist[j]; 
                HeapInsert(N,H); 
            } 
        } 
        if (H->last != 0) 
            DeleteMin (H, &E); 
        else 
        break; 
    } 
} 
void GreateGraph(Graph &graph){ 
    int i; 

    printf("请输入顶点数"); 
    scanf("%d",&graph.n); 
    graph.nod = (Node *) malloc (graph.n * sizeof(Node)); 
    if (graph.nod == NULL){ 
        printf("内存分配失败\n"); 
    exit(-1); 
} 
    graph.prev = (int *) malloc (graph.n * sizeof(int)); 
    if (graph.prev == NULL){ 
        printf("内存分配失败\n"); 
        exit(-1); 
    } 
    graph.dist = (int *) malloc (graph.n * sizeof(int)); 
    if (graph.dist == NULL){ 
        printf("内存分配失败\n"); 
        exit(-1); 
    } 
    graph.c = (int **) malloc (graph.n * sizeof(int)); 
    for (i = 0; i < graph.n; i++) 
    graph.c[i] = (int *) malloc (graph.n * sizeof(int)); 
} 
void DestroyGraph(Graph graph){ 
    int i; 
    for (i = 0; i< graph.n; i++) 
        free(graph.c[i]); 
    free(graph.c); 
    graph.c = NULL; 
    free(graph.nod); 
    graph.nod = NULL; 
    free(graph.dist); 
    graph.dist = NULL; 
    free(graph.prev); 
    graph.prev = NULL; 
} 
void ShowDist(Graph G, int u){ 
    int i, k; 

    for (i = 0; i < G.n; i++) { 
        printf("\n源点u=%d到顶点%d的最短路程为%d", u, i, G.dist[i]); 
        printf("\n其路径为"); 
        k = i; 
        while (G.prev[k] != -1){ 
            printf(" %d ", G.prev[k]); 
            k = G.prev[k]; 
        } 
    } 
    printf("\n\n"); 
    for (i = 0; i < G.n; i++) 
        printf(" %d ",G.dist[i]); 
    printf("\n\n\n"); 
    for (i = 0; i < G.n; i++) 
        printf(" %d ",G.prev[i]); 
} 
int main(void){ 
    Graph graph; 
    int u; 
    printf("请输入源点u"); 
    scanf("%d", &u); 
    GreateGraph(graph); 
    InitGraph(graph); 
    ShortestPaths(graph,u); 
    ShowDist(graph,u); 
    DestroyGraph(graph); 
    return 0; 
}