強連通分量 floyd算法 每日練習

先來一道熱身題,藍橋杯 算法提高ADV-284 GPA
1.問題描述
  輸入A,B兩人的學分獲取情況,輸出兩人GPA之差。
輸入格式
  輸入的第一行包含一個整數n表示A的課程數,以下n行每行Si,Ci分別表示第i個課程的學分與A的表現。
  GPA=Σ(Si*Ci) / Σ(Si)。
  特殊地,如果Ci是’P’或者’N’(對應於通過與不通過),則第i個課程不記入GPA的計算(即當其不存在)。
  A讀入結束後讀入B,B的輸入格式與A相同。
  保證2人的Σ(Si)非零
輸出格式
  輸出A的GPA - B的GPA的值,保留2位小數(四捨五入)
  Tips:當A和B的分數相近時輸出0.00。
 代碼如下,注意A和B的分數差小於0.01的時候就要輸出零

#include<iostream>
#include<string>
#include<cmath>
using namespace std;

int main() {
	int n;			//學科數目 
	float Sa=0, Sb=0;			//學分 
	float Ca = 0, Cb = 0;		//學分績點 
	char c[5];
	int s;
	float result_a, result_b;

	scanf("%d", &n);
	for(int i=0;i<n;i++){		//計算Sa、Ca
		scanf("%d", &s);
		scanf("%s", c);
		if (c[0] != 'P'&&c[0] != 'N') {		//如果是P或者N,就不計入
			Sa += s;
			int tmp = 0;
			for (int i = 0; ; i++) {		//將字符串轉換爲十進制數字
				if (c[i] == '\0')
					break;
				tmp = tmp * 10 + c[i] - 48;
			}
			Ca += tmp * s;
		}
	}

	scanf("%d", &n);		//計算Sb、Cb
	for (int i = 0; i < n; i++) {
		scanf("%d", &s);
		scanf("%s", &c[0]);
		if (c[0] != 'P'&&c[0] != 'N') {
			Sb += s;
			int tmp = 0;
			for (int i = 0; ; i++) {
				if (c[i] == '\0')
					break;
				tmp = tmp * 10 + c[i] - 48;
			}
			Cb += tmp * s;
		}
	}
	result_a = Ca / Sa;		//printf輸出的時候默認四捨五入
	result_b =Cb / Sb;
	if (result_a - result_b<=0.01&& result_a - result_b >= -0.01)	//這裏必須這樣寫,否則一個測試用例過不了,無須在意這裏
		printf("0.00");
	else
		printf("%.2f",result_a - result_b);
	return 0;
}

一開始我用的string來替代char c[],但是運行的時候出錯了,原因是用string的時候,如果用cin讀入cout輸出,沒有問題,如果用scanf讀入,必須這樣:

	scanf("%s",&c[0]);

輸出的時候如果用printf,要這樣:

	printf("%s", c.c_str());

2.Kosaraju算法
該算法用來求有向圖的強連通分量,基本思想是做兩次dfs,第一次正向dfs,然後按照遞歸回退的順序記錄頂點(越後優先級越高),然後取優先級最高的頂點u,作爲起點,反向dfs,因爲原本與u同處一個強連通分量的頂點,就算邊方向了,也還是能訪問到,而那些與u不屬於一個強連通分量的頂點,因爲第一次dfs回退的順序就是該圖拓撲排序的一個逆序,每次取優先級最高的頂點,就是取圖的”開始部分“,因爲邊都反向了,故不可能遍歷到和其不是一個強連通分量的頂點。繼續取優先級最高的頂點,繼續上述操作,直到遍歷所有頂點。Kosaraju算法的複雜度是O(V+E)。
下面以hdu1269 迷宮城堡 爲例,給出代碼:

//hdu 1269 迷宮城堡
//一個有向圖,有n-1個點(n<=10000)和m條邊(m<=100000)。判斷整個圖是否強連通,如果是,輸出Yes,否則輸出No。 
#include<stdio.h>
#include<vector>
#include<cstring>
using namespace std;

const int NUM = 10005;
vector<int> G[NUM], rG[NUM];
vector<int> S;			//存第一次dfs1()的結果,並標記點的先後順序
int vis[NUM], sccno[NUM], cnt;		//cnt是強連通分量的個數

void dfs1(int u) {
	if (vis[u])
		return;
	vis[u] = 1;
	for (int i = 0; i < G[u].size(); i++)
		dfs1(G[u][i]);
	S.push_back(u);			//記錄點的先後順序,標記大的放在S的後面
}
void dfs2(int u) {
	if (sccno[u])
		return;
	sccno[u] = cnt;
	for (int i = 0; i < rG[u].size(); i++)
		dfs2(rG[u][i]);
}
void Kosaraju(int n) {
	cnt = 0;
	S.clear();
	memset(sccno, 0, sizeof(sccno));
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= n; i++)
		dfs1(i);				//點的編號:1~n 遞歸所有點
	for (int i = n - 1; i >= 0; i--)
		if (!sccno[S[i]]) {
			cnt++;
			dfs2(S[i]);
		}
}

int main() {
	int n, m, u, v;
	while (scanf("%d%d", &n, &m), n != 0 || m != 0) {
		for (int i = 1; i <= n; i++) {
			G[i].clear();
			rG[i].clear();
		}
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &u, &v);
			G[u].push_back(v);			//原圖
			rG[v].push_back(u);			//反圖

		}
		Kosaraju(n);
		printf("%s\n", cnt == 1 ? "Yes" : "No");
	}
	return 0;
}

3.floyd算法
floyd算法可以一次求出所有結點間的最短路徑,而且程序很簡單。其缺點就是效率不高,複雜度達到了O(n^3),只能在n<200這樣的小規模情況下使用。
floyd用到了動態規劃的思想:求兩點i、j之間的最短距離,可以分爲兩種情況,即經過圖中某個點k的路徑和不經過點k的路徑,取兩者中的最短路徑。

floyd算法 
#include<stdio.h>
using namespace std;

const int INF=1e6;
const int NUM=105;
int graph[NUM][NUM];
int n,m;

void floyd(){
	int s=1;
	for(int k=1;k<=n;k++)			//遍歷每個中轉站
		for(int i=1;i<=n;i++)		//i和j用來遍歷每個路徑
			if(graph[i][k]!=INF)
				for(int j=1;j<=n;j++)
					if(graph[i][j]>graph[i][k]+graph[k][j])
							graph[i][j]=graph[i][k]+graph[k][j];
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		if(n==0&&m==0)
			return 0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)		//初始化
				if(i==j)
					graph[i][j]=0;
				else
					graph[i][j]=INF;
					
		while(m--){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);	
			graph[a][b]=graph[b][a]=c;
		}
		floyd();	
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)
				printf("%3d",graph[i][j]);
			printf("\n");
		}
	}
	return 0;
}

關於floyd算法,這裏有個很好的文章,講解很透徹,生動精闢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章