1. 程式人生 > >TSP 旅行商

TSP 旅行商

THUOJ 資料結構(上)TSP 旅行商


實現思路

建立鄰接表

  • 每讀入一條邊u->v,將其插入u中(後面將實現的tspNode中的邊,是以其為出發點的邊),並將v的入度+1

    拓撲排序過程中計算最長道路經過的村莊數

  • 演算法:零入度拓撲排序,p166 of 資料結構(c++語言版)_鄧俊輝
  1. 掃描所有節點,入度為0的入棧
  2. 從棧頂開始,每讀一個節點a,掃描其邊,相連節點b入度-1,ind變為0的入棧;
    更新b'路徑長度=max(a's+1,b');更新max路徑;

    邊界情況處理

  • 由題意
    1 ≤ n ≤ 1,000,000
    0 ≤ m ≤ 1,000,000
    主要邊界情況為:路徑數量m=0時,此時應該輸出1

面向物件

  • 以往做oj題時,只追求執行效率和程式碼簡潔,未按照面向物件思維開發,得不償失,此次首次嘗試面向物件,讓我們注重培養計算思維和抽象思維能力吧

    1.實現堆疊模板類stack

ADT
#define DEFAULT_CAPACITY 10
template <typename T>
class stack
{
  private:
    T *st;
    int capacity;
    int top;
    void expand();//未真正實現,整合在了push中
  public:
    //建構函式
    stack(int c = DEFAULT_CAPACITY)//例項化一個容量為c的堆疊
    //解構函式
    ~stack() { delete[] st; }
    int getsize() const { return top; }//top恰為元素個數
    bool empty() { return top == 0; }
    void push(T a);
    T pop();
};

2.tsp節點的ADT

//邊表結點
struct edgeNode
{
    int adjvex; // 鄰接點域
    struct edgeNode *next;
};

//TSP節點
template <typename T>
class tspNode
{
  public:
    int ind, road;                       //為了方便把ind定義為public了
    void inserte(int i);                 //insert edge 插入指向節點i的邊
    int indegree() const { return ind; } //讀取入度
    int outdegree() const { return outd; }
    edgeNode *getfirstedge() { return firstedge; }
    //建構函式
    tspNode()
    {
        ind = 0;
        outd = 0;
        road = 1;
        firstedge = NULL;
    }

  private:
    int outd; //outdegree
    edgeNode *firstedge;
};
  • 像陣列一樣使用模板類
    tspNode<int> *tsp = new tspNode<int>[n + 1];
    delete[] tsp;
    //或形如 tspNode<int> tsp[10];

原始碼

tsp.cpp
#include <cstdio>
#include "tsp.h"
const int stackSize = 1000;
int main()
{
    int n, m;

    scanf("%d %d", &n, &m);
    if (m == 0)
    {
        printf("%d\n", 1);
        return 0;
    }
    tspNode<int> *tsp = new tspNode<int>[n + 1];
    int u, v;
    for (int i = 0; i < m; i++)
    {
        scanf("%d", &u);
        scanf("%d", &v); //u->v
        tsp[u].inserte(v);
        tsp[v].ind++;
    } //初始化
    stack<int> stack(stackSize);
    for (int i = 1; i <= n; i++)
    {
        if (tsp[i].indegree() == 0)
            stack.push(i); //遇到入度為0的,將其秩入棧
    }
    int a = 0, b = 0, maxroad = 0;
    edgeNode *p;
    while (!stack.empty())
    {
        a = stack.pop();
        p = tsp[a].getfirstedge();
        while (p != NULL)
        {
            b = p->adjvex;
            tsp[b].ind--;
            if (tsp[b].road < 1 + tsp[a].road)
            {
                tsp[b].road = 1 + tsp[a].road;
            }
            if (maxroad < tsp[b].road)
                maxroad = tsp[b].road;
            if ((tsp[b].ind) == 0)
                stack.push(b);
            p = p->next;
        }
        //從棧頂開始,每讀一個節點a,掃描其邊,相連節點b入度-1,b'路徑長度=max(a's+1,b')並更新max路,ind變為0的入棧,
    }
    printf("%d\n", maxroad);
    delete[] tsp;
    return 0;
}

tsp.h
#include <cstdio>

//邊表結點
struct edgeNode
{
    int adjvex; // 鄰接點域
    struct edgeNode *next;
};

//TSP節點
template <typename T>
class tspNode
{
  public:
    int ind, road;                       //為了方便把ind定義為public了
    void inserte(int i);                 //insert edge 插入指向節點i的邊
    int indegree() const { return ind; } //讀取入度
    int outdegree() const { return outd; }
    edgeNode *getfirstedge() { return firstedge; }

    tspNode()
    {
        ind = 0;
        outd = 0;
        road = 1;
        firstedge = NULL;
    }

  private:
    int outd; //outdegree
    edgeNode *firstedge;
};

template <typename T>
void tspNode<T>::inserte(int e)
{
    edgeNode *s = new edgeNode;
    s->adjvex = e;
    s->next = firstedge;
    firstedge = s;
    outd++;
};

#define DEFAULT_CAPACITY 10
template <typename T>
class stack
{
  private:
    T *st;
    int capacity;
    int top;
    void expand();

  public:
    stack(int c = DEFAULT_CAPACITY)
    {
        st = new T[capacity = c];
        top = 0;
    }
    ~stack() { delete[] st; }
    int getsize() const { return top; }
    bool empty() { return top == 0; }
    void push(T a)
    {
        st[top++] = a;
        if (top == capacity)
        {
            T *oldst = st;
            st = new T[capacity <<= 1];
            for (int i = 0; i < top; i++)
                st[i] = oldst[i];
            delete[] oldst;
        }
    }
    T pop() { return st[--top]; }
};

複雜度分析

  • 鄰接表初始化:O(m)
  • 拓撲排序:掃描入度為0的點O(n)
  • 零入度演算法:單次操作相當於刪除一條邊,O(m)
  • 堆疊擴容:分攤不過O(1),總共不過O(n)
  • 結論:時間複雜度O(m+n),空間消耗為鄰接表和堆疊,也為O(m+n)