1. 程式人生 > >圖的整理與總結

圖的整理與總結

1.dijkstra演算法

①求從source到destination的最短路徑(最短距離、最短路徑條數、最短路徑上的節點數以及路徑)

②若最短路徑不唯一,則再求從source到destination的最小代價

③若最短路徑還是不唯一,則再求從source到destination的最大節點權值和

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <climits>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>  
#include <set>
using namespace std;

#define INF 0x3f3f3f3f
#define MAXN 500 

int d[MAXN][MAXN];  //d[i][j]-從i到j的距離 (由題目給出) 
int c[MAXN][MAXN];  //c[i][j]-從i到j的代價 (由題目給出)
int w[MAXN];        //w[i]-i的權值(由題目給出)
int dis[MAXN];      //dis[i]-從起點到i的最短距離 
int cost[MAXN];     //cost[i]-從起點到i的最小代價 
int maxw[MAXN];     //maxw[i]-從起點到i的最大權值 
int cnt[MAXN];      //cnt[i]-從起點到i的最短路徑條數 
int num[MAXN];      //num[i]-從起點到i的最短路徑上的節點個數 
int pre[MAXN];      //pre[i]-最短路徑上i的前一個節點 
int vis[MAXN];      //vis[i]-i是否被訪問過 

void dijkstra(int source, int destination, int n)  //source-起點 destination-終點 n-節點數 
{
	for (int i = 0; i < n; i++)
	{
		dis[i] = INF;
		cost[i] = INF;
		maxw[i] = 0;
		cnt[i] = 0;
		num[i] = 0;
		pre[i] = -1;
		vis[i] = 0;
	}
	dis[source] = 0;
	cost[source] = 0;
	maxw[source] = w[source];
	cnt[source] = 1;
	num[source] = 1;
	for (int i = 0; i < n; i++)
	{
		int min = INF, k = -1;
		for (int j = 0; j < n; j++)
		{
			if (!vis[j] && dis[j] < min)
			{
				min = dis[j];
				k = j;
			}
		}
		if (k == -1)  //沒有符合條件的節點則返回 
			return;
		vis[k] = 1;
		if (k == destination)  //終點已求得最短路徑則返回 
			return;
		for (int j = 0; j < n; j++)
		{
			if (!vis[j] && d[k][j] != INF)
			{
				if (dis[k] + d[k][j] < dis[j])  //從起點經過k到j的距離比從起點到j的距離短 
				{
					dis[j] = dis[k] + d[k][j];
					cost[j] = cost[k] + c[k][j];
					maxw[j] = maxw[k] + w[j];
					cnt[j] = cnt[k];
					num[j] = num[k] + 1;
					pre[j] = k;
				}
				else if (dis[k] + d[k][j] == dis[j])
				{
					cnt[j] += cnt[k];
					if (cost[k] + c[k][j] < cost[j])  //在距離相同的情況下,從起點經過k到j的代價比從起點到j的代價小 
					{
						cost[j] = cost[k] + c[k][j];
						maxw[j] = maxw[k] + w[j];
						num[j] = num[k] + 1;
						pre[j] = k;
					}
					else if (cost[k] + c[k][j] == cost[j] && maxw[k] + w[j] > maxw[j])  //在距離和代價相同的情況下,從起點經過k到j的節點權值比從起點到j的節點權值大  
					{
						maxw[j] = maxw[k] + w[j];
						num[j] = num[k] + 1;
						pre[j] = k;
					}
				}
			}
		}
	}
}

void print_path(int source, int x)  //遞迴輸出最短路徑(不包括起點,因為輸出第一個節點的時候不需要"->") 
{
	if (x == source)
		return;
	print_path(source, pre[x]);
	cout << "->" << x;
}

int main()
{
	int n, m;
	cin >> n >> m;  //n-節點數 m-邊數 
	for (int i = 0; i < n; i++)
	{
		int v, ww;
		cin >> v >> ww;  //v-節點號 ww-權值 
		w[v] = ww;
	}
	memset(d, INF, sizeof(d));
	memset(c, INF, sizeof(c));
	for (int i = 0; i < m; i++)
	{
		int v, u, dd, cc;
		cin >> v >> u >> dd >> cc;  //v-邊的起點 u-邊的終點 dd-邊的距離 cc-邊的代價 
		d[v][u] = d[u][v] = dd;
		c[v][u] = c[u][v] = cc;
	}
	int source, destination;
	cin >> source >> destination;
	dijkstra(source, destination, n);
	cout << dis[destination] << " " << cost[destination] << " " << maxw[destination] << " " << cnt[destination] << " " << num[destination] << endl;
	cout << source;
	print_path(source, destination);
	return 0;
}

2.並查集

一般用於:

①求該圖的連通分支

②判斷該圖是否有環

③Kruskal演算法

#define MAXN 1000

int f[MAXN];

void Init()  //初始化每個節點的祖先都是自己
{
	for (int i = 1; i <= n; i++)
		f[i] = i;
}

int Find(int x)  //查詢節點x的祖先
{
	if (x != f[x])
		f[x] = Find(f[x]);  //回溯時壓縮路徑,讓每一個節點直接和其祖先連線
	return f[x];
}

void Union(int x, int y)  //合併兩個分支
{
	int a = Find(x);
	int b = Find(y);
	if (a != b)
		f[a] = b;

}