圖演算法小結(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;
}