1. 程式人生 > 其它 >圖練習記錄

圖練習記錄

技術標籤:PTA/MOOC類題 (C語言/C++)圖論演算法

7-1兩點間有路徑嗎?(20分)

對於給定的無向圖以及圖中的兩個頂點,計算兩個頂點所在的連通分量中的頂點數,並且判斷這兩個頂點之間是否有路徑。

輸入格式:

第一行是不超過20的正整數N,表示圖有N個頂點,頂點的編號即0~N-1

接下來N行,是N*N的鄰接矩陣,矩陣的元素間用空格分隔;

最後一行是用空格隔開的兩個頂點編號vw

輸出格式:

第一行輸出v所在的連通分量的頂點數

第二行輸出w所在的連通分量的頂點數

第三行,若vw之間有路徑,則輸出Yes,否則輸出No

注意:當vw是同一個頂點時,認為vw之間是有路徑的。

輸入樣例:

對於這個圖:fig.jpg

8
0 1 1 0 0 0 0 1
1 0 0 0 1 0 0 0
1 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 1 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
1 3

輸出樣例:

5
2
No


用並查集就挺方便的

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int edges[30][30];
int fa[30];
int cnt[30]; 

void init() {
	memset(edges, 0, sizeof(edges));
	for(int i = 0; i < 30; i++) {
		fa[i] = i; 
		cnt[i] = 1; //一開有自己這個本身元素
	}
}

int find(int x) {
	int fa_x = fa[x];
	while (fa[fa_x] != fa_x) {
		fa_x = fa[fa_x];
	}
	fa[x] = fa_x; //這一步可以記憶此時x最往上的根是fa_x,這樣下一次再要用到fa[x]時,可以更快找到其最往上的根 
	return fa_x;
}

void Union(int x, int y) {
	int fa_x = find(x);
	int fa_y = find(y);
	if (fa_x != fa_y) {
		fa[fa_y] = fa_x;
		cnt[fa_x] += cnt[fa_y];
	}
}

int main()
{
	int n;
	cin >> n;
	init();
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < n; j++) {
			int t;
			cin >> t;
			edges[i][j] = t;
			if (edges[i][j] == 1 && edges[j][i] != 1 
			&& find(i) != j && find(j) != i) 
				Union(i, j);
		}
	}
	int v, w;
	cin >> v >> w;
	int fa_v = find(v);
	int fa_w = find(w);
	//cout << fa_v << endl;
	//cout << fa_w << endl;
	//for(int i = 0; i < 30; i++) cout << i << " " << cnt[i] << endl;
	cout << cnt[fa_v] << endl;
	cout << cnt[fa_w] << endl;
	
	if (fa_v == fa_w) cout << "Yes" << endl;
	else cout << "No" << endl;
	
	return 0;
}
			
	

7-2功夫傳人(25分)

一門武功能否傳承久遠並被髮揚光大,是要看緣分的。一般來說,師傅傳授給徒弟的武功總要打個折扣,於是越往後傳,弟子們的功夫就越弱…… 直到某一支的某一代突然出現一個天分特別高的弟子(或者是吃到了靈丹、挖到了特別的祕笈),會將功夫的威力一下子放大N倍 —— 我們稱這種弟子為“得道者”。

這裡我們來考察某一位祖師爺門下的徒子徒孫家譜:假設家譜中的每個人只有1位師傅(除了祖師爺沒有師傅);每位師傅可以帶很多徒弟;並且假設輩分嚴格有序,即祖師爺這門武功的每個第i代傳人只能在第i-1代傳人中拜1個師傅。我們假設已知祖師爺的功力值為Z,每向下傳承一代,就會減弱r%,除非某一代弟子得道。現給出師門譜系關係,要求你算出所有得道者的功力總值。

輸入格式:

輸入在第一行給出3個正整數,分別是:N(≤10​5​​)——整個師門的總人數(於是每個人從0到N−1編號,祖師爺的編號為0);Z——祖師爺的功力值(不一定是整數,但起碼是正數);r——每傳一代功夫所打的折扣百分比值(不超過100的正數)。接下來有N行,第i行(i=0,⋯,N−1)描述編號為i的人所傳的徒弟,格式為:

K​i​​ID[1] ID[2]⋯ID[K​i​​]

其中K​i​​是徒弟的個數,後面跟的是各位徒弟的編號,數字間以空格間隔。K​i​​為零表示這是一位得道者,這時後面跟的一個數字表示其武功被放大的倍數。

輸出格式:

在一行中輸出所有得道者的功力總值,只保留其整數部分。題目保證輸入和正確的輸出都不超過10​10​​。

輸入樣例:

10 18.0 1.00
3 2 3 5
1 9
1 4
1 7
0 7
2 6 1
1 8
0 9
0 4
0 3

輸出樣例:

404



程式碼如下

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

vector<vector<int>> edges(100010);
double ans; //所有得道者的功力總值 

void dfs(int cur, double z, double r) {
	for(int i = 0; i < edges[cur].size(); i++) {
		if (edges[cur][0] == 0) { //得道者,因為題目都沒有給出得道者的編號,所以自然無法得知得道者的徒弟 
			ans += edges[cur][1] * z;
			return ;
		} else { //正常徒弟 
			dfs(edges[cur][i], z - z * r / 100, r);
		}
	}
}
			
int main()
{
	int n;
	double z, r;
	cin >> n >> z >> r;
	for(int i = 0; i < n; i++) {
		int t, num;
		cin >> t;
		if (t == 0) { //得道者 
			edges[i].push_back(0);
			cin >> num;
			edges[i].push_back(num);
		} else { //正常徒弟
			while (t--) {
				cin >> num;
				edges[i].push_back(num);
			}
		}
	}
	
	dfs(0, z, r);
	cout << (int)ans << endl;
	
	return 0;
}