1. 程式人生 > >圖演算法小結(prime與dijkstra對比) CodeBlocks再次安裝

圖演算法小結(prime與dijkstra對比) CodeBlocks再次安裝

一:CodeBlocks再次安裝,無法編譯問題

(0)最近自己自行從網上下載了一個CodeBlocks進行安裝(ps竟然下載了一個不帶有任何編譯器的裸的exe檔案;如果想用GUN GCC編譯器,即缺少MinGW資料夾),下載即可,或者下載一個帶有MinGW編譯器的安裝包。如果安裝了帶編譯器的codeblocks,還是不能編譯,那可能就是設定問題了:
第一步:開啟codeblocks , 點選單中的 setting , 選擇 compiler and debugger。會彈出一個設定頁

第二步:請確認右邊的compiler 選擇的是GNU GCC Compiler.下面有幾個標籤頁,分別是Compiler setting , Linker setting ...  

第三步:選擇第四個標籤頁 Toolchain executables 這裡要設定編譯器的目錄。可以先點右邊的auto detect,讓編譯器自動探測設定。

如果探測到了,彈出的框裡會寫 Auto-detected installation path of "GNU GCC Compiler" in "C:\...";如果沒有探測到,也會彈出一個框給出提示。

如果自己知道編譯器的目錄,可以自己選擇,點auto detect 旁邊的三個點的按鈕,自己選擇目錄就行了。

二:Dijstra 最短路徑和prim最小生成樹演算法

(0)Dijstra 最短路徑和prim最小生成樹演算法,神似,只是在更新dist時的if條件不同;主要是這種prime 的計算兩個集合間的最小值的思想非常重要。

(1)某省自從實行了很多年的暢通工程計劃後,終於修建了很多路。不過路多了也不好,每次要從一個城鎮到另一個城鎮時,都有許多種道路方案可以選擇,而某些方案要比另一些方案行走的距離要短很多。這讓行人很困擾。
現在,已知起點和終點,請你計算出要從起點到終點,最短需要行走多少距離。
Input
本題目包含多組資料,請處理到檔案結束。
每組資料第一行包含兩個正整數N和M(0<N<200,0<M<1000),分別代表現有城鎮的數目和已修建的道路的數目。城鎮分別以0~N-1編號。
接下來是M行道路資訊。每一行有三個整數A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城鎮A和城鎮B之間有一條長度為X的雙向道路。
再接下一行有兩個整數S,T(0<=S,T<N),分別代表起點和終點。
Output
對於每組資料,請在一行裡輸出最短需要行走的距離。如果不存在從S到T的路線,就輸出-1.

Sample Input
3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2

Sample Output
2
-1

// prime 和 dij演算法的核心 —— 如何保證當前的最小邊是最終的最小邊,
// 上面試最小生成樹的演算法,下面是最短路徑的演算法了
#include <iostream>
#include <cstring>
using namespace std;

const int INF = 1000000;
const int MAX_SIZE = 200;
int map[MAX_SIZE][MAX_SIZE];
bool visited[MAX_SIZE];
int disit[MAX_SIZE]; //儲存距離

//s開始位置,e結束位置
void dij_method(int s, int e, int N)
{
    // init the params of dij
    memset(visited, false, sizeof(visited));
    for (int i = 0; i < N; i++)
    {
        if (map[s][i] == -1)
            disit[i] = INF;
        else
            disit[i] = map[s][i];
    }
    // 從s 點開始計算
    visited[s] = true;
    for (int i = 1; i < N; i++)
    {
        int min = INF;
        int index = 0;
        // 找當前最小的 (這與prime一樣,只是if條件更強了)
        for (int j = 0; j < N; j++)
        {
            if (!visited[j] && j != s && disit[j] != -1 && disit[j] < min)
            {
                min = disit[j];
                index = j;
            }
        }
        visited[index] = true;//頂點併入U集
        if (index == e) //如果已經達到終點
            break;
        // 調整更新目前的最短距離(這與這與prime一樣,只是if條件不一樣而已)
        for (int j = 0; j < N; j++)
        {
            if (!visited[j] && map[index][j] != -1
                    && (min + map[index][j]) < disit[j])
            {
                disit[j] = min + map[index][j];
            }
        }// end of for
    }// end of for
}

int main()
{
    int N, M;// 點數和記錄數
    int ori, des, len;//
    int start, end;
    while (cin >> N >> M)
    {
        memset(map, -1, sizeof(map)); //-1表示不可達
        while (M--)
        {
            cin >> ori >> end >> len;
            //有可能有更小的路徑
            if (map[ori][des] == -1 || len < map[ori][des])
            {
                map[ori][des] = len;
                map[des][ori] = len;
            }
        }
        // input and input start and end.
        cin >> start >> end;

        if (start == end)
        {
            cout << 0 << endl;
            continue;
        }
        // 呼叫無負權邊的圖(Dijkstra演算法)
        dij_method(start, end, N);

        if (visited[end] && disit[end] < INF)
            cout << disit[end] << endl;
        else
            cout << -1 << endl;
    }
    return 0;
}

(2)* About:    有向圖的Dijkstra演算法實現
輸入資料:
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
輸出資料:
999999 10 999999 30 100
10 999999 50 999999 999999
999999 50 999999 20 10
30 999999 20 999999 60
100 999999 10 60 999999
源點到最後一個頂點的最短路徑長度: 60
源點到最後一個頂點的路徑為: 1 -> 4 -> 3 -> 5

#include <iostream>
using namespace std;
 
const int maxnum = 100;
const int maxint = 999999;
 
 
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
    bool s[maxnum];    // 判斷是否已存入該點到S集合中
    for(int i=1; i<=n; ++i)
    {
        dist[i] = c[v][i];
        s[i] = 0;     // 初始都未用過該點
        if(dist[i] == maxint)
            prev[i] = 0;
        else
            prev[i] = v;
    }
    dist[v] = 0;
    s[v] = 1;
 
    // 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中
    // 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度
    for(int i=2; i<=n; ++i)
    {
        int tmp = maxint;
        int u = v;
        // 找出當前未使用的點j的dist[j]最小值
        for(int j=1; j<=n; ++j)
            if((!s[j]) && dist[j]<tmp)
            {
                u = j;              // u儲存當前鄰接點中距離最小的點的號碼
                tmp = dist[j];
            }
        s[u] = 1;    // 表示u點已存入S集合中
 
        // 更新dist
        for(int j=1; j<=n; ++j)
            if((!s[j]) && c[u][j]<maxint)
            {
                int newdist = dist[u] + c[u][j];
                if(newdist < dist[j])
                {
                    dist[j] = newdist;
                    prev[j] = u;
                }
            }
    }
}
 
void searchPath(int *prev,int v, int u)
{
    int que[maxnum];
    int tot = 1;
    que[tot] = u;
    tot++;
    int tmp = prev[u];
    while(tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = prev[tmp];
    }
    que[tot] = v;
    for(int i=tot; i>=1; --i)
        if(i != 1)
            cout << que[i] << " -> ";
        else
            cout << que[i] << endl;
}
 
int main()
{
    freopen("input.txt", "r", stdin);
    // 各陣列都從下標1開始
    int dist[maxnum];     // 表示當前點到源點的最短路徑長度
    int prev[maxnum];     // 記錄當前點的前一個結點
    int c[maxnum][maxnum];   // 記錄圖的兩點間路徑長度
    int n, line;             // 圖的結點數和路徑數
 
    // 輸入結點數
    cin >> n;
    // 輸入路徑數
    cin >> line;
    int p, q, len;          // 輸入p, q兩點及其路徑長度
 
    // 初始化c[][]為maxint
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=n; ++j)
            c[i][j] = maxint;
 
    for(int i=1; i<=line; ++i)  
    {
        cin >> p >> q >> len;
        if(len < c[p][q])       // 有重邊
        {
            c[p][q] = len;      // p指向q
            c[q][p] = len;      // q指向p,這樣表示無向圖
        }
    }
 
    for(int i=1; i<=n; ++i)
        dist[i] = maxint;
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
            printf("%8d", c[i][j]);
        printf("\n");
    }
 
    Dijkstra(n, 1, dist, prev, c);
 
    // 最短路徑長度
    cout << "源點到最後一個頂點的最短路徑長度: " << dist[n] << endl;
 
    // 路徑
    cout << "源點到最後一個頂點的路徑為: ";
    searchPath(prev, 1, n);
}

(3)

//poj2367題意: 知道一個數n, 然後n行,編號1到n, 每行輸入幾個數,
//該行的編號排在這幾個數前面,輸出一種符合要求的編號名次排序。
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;
const int MAXN = 105;

bool adj[MAXN][MAXN];// 林街邊
int in_degree[MAXN];// 入度
int result[MAXN];// 結果順序

void topo_sort(const int n)
{
    int i,j,k;
    memset(in_degree,0,sizeof(in_degree));
    for(i=1; i<=n; i++)   //尋找入度為零的節點
    {
        for(j=1; j<=n; j++)
        {
            if(adj[i][j])
                in_degree[j]++;
        }
    }// 可以在輸入的時候就計算入度的數值

    for(i=1; i<=n; i++)// 這一個i表示個數的
    {
        for(j=1; j<=n; j++)
        {
            if(in_degree[j]==0)
            {
                k=j;
                break;
            }
        }
        in_degree[k]=-1;// 標誌,已訪問過
        result[i]=k;
        for(j=1; j<=n; j++)  //入度減一
        {
            if(adj[k][j])
            {
                in_degree[j]--;
            }
        }// end of for
    }// end of for
}

int main()
{
    int i,tem;
    int n;
    //init and input
    memset(adj,false,sizeof(adj));
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    {
         while(scanf("%dtem",&tem),tem)
         {
             adj[i][tem]=true;
         }
    }
    // topo_sort
    topo_sort(n);
    for(i=1; i<=n; i++)
    {
        if(i==1)
             printf("%d",result[i]);
        else
            printf(" %d",result[i]);
    }
    printf("\n");
    return 0;
}
(4)

POJ 3249 拓撲排序+動態規劃
分類: ACM 2012-06-11 13:38 812人閱讀 評論(0) 收藏 舉報
inputdelete儲存struct
該題是讓求在一個有向無環圖中,從一個入度為 0 的節點出發,到一個出度為 0 的節點的最大的權值問題。我們可以使用廣搜,但是會超時,上網查了一下解題報告,可以使用拓撲排序+動態規劃來解決此問題:
dp[1] = max{ dp[2] + cost[1] , dp[3] + cost[1] };
dp[2] = cost[2] + dp[4];
dp[3] = cost[3] + dp[4];
dp[4] = cost[4];
dp[5] = cost[5] + dp[6];
dp[6] = cost[6];
在拓撲排序的過程中,是入度為0的節點先入佇列,所以我們可以做一個逆置思考,是的 dp[i] 儲存的是入度為零的節點到當前節點的最大權值和,而最後在出度為 0 的節點的 dp[i] 中搜索最大的權值。程式碼實現如下:

#include <iostream>
#include <queue>
#include <cstdio>

using namespace std;
#define N 100100
#define M 1000100
#define INF 1<<29

struct node
{
    int v;
    int next;
} edge[M];

int dp[N];  // 最大價值 儲存 由 拓撲排序後 入度為0的節點到當前節點最大的 profit
int enext[N];   //記錄節點 i 當前的邊,由當前的邊 推出 下一條邊
int indegree[N];    //入度
int cost[N];    //價值
int res;
int idx;
std::queue<int > q;

int m,n,a,b,i,j;
void input()
{
    for(i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        dp[i] = -INF;
        indegree[i] = 0;
    }

    idx=0;

    //memset(dp,-INF,sizeof(int)*(n+10) );
    //memset(indegree,0,sizeof(int)*(n+10) );    //節點度數初始化為0
    memset(enext,-1,sizeof(enext) ); //說明當前節點只此一條邊

    for(i=1;i<=m;i++)
    {
        scanf("%d %d",&a,&b);
        edge[idx].v = b;
        edge[idx].next = enext[a];
        enext[a] = idx++;
        indegree[b]++;
    }
    while( !q.empty() )
        q.pop();
    for( i=1;i<=n;i++)
        if( !indegree[i] )
            q.push(i),dp[i]=cost[i];
}


int dag()
{
    res = -INF;
    while( !q.empty() )
    {
        int cur = q.front();
        q.pop();
        bool flag = true;  //只有節點的出度為 0 的時候,我們才判斷其是否有最大profit
        int nextid;
       // cout<<"cur : "<<cur<<endl;
        for( i=enext[cur] ; i != -1 ; i = edge[i].next) //遍歷當前頂點的所有的邊
        {
            flag = false;
            nextid =  edge[i].v ;

        //    cout<<"nextid : "<<nextid<<endl;
            if( dp[nextid] < cost[ nextid ] + dp[cur])
                dp[nextid] =  cost[nextid] + dp[cur];
            indegree[nextid]--;
            if( !indegree[nextid] )
            {
                q.push(nextid);
            }
        }
        if( flag && dp[cur] > res )
            res = dp[cur];
    }
    return res;
}

int main()
{
    while(scanf("%d %d",&n,&m) != EOF)
    {
        input();
        printf("%d\n",dag());
    }
    return 0;
}