圖練習記錄
技術標籤:PTA/MOOC類題 (C語言/C++)圖論演算法
7-1兩點間有路徑嗎?(20分)
對於給定的無向圖以及圖中的兩個頂點,計算兩個頂點所在的連通分量中的頂點數,並且判斷這兩個頂點之間是否有路徑。
輸入格式:
第一行是不超過20
的正整數N
,表示圖有N
個頂點,頂點的編號即0
~N-1
;
接下來N
行,是N*N
的鄰接矩陣,矩陣的元素間用空格分隔;
最後一行是用空格隔開的兩個頂點編號v
和w
輸出格式:
第一行輸出v
所在的連通分量的頂點數
第二行輸出w
所在的連通分量的頂點數
第三行,若v
和w
之間有路徑,則輸出Yes
,否則輸出No
注意:當v
和w
是同一個頂點時,認為v
和w
之間是有路徑的。
輸入樣例:
對於這個圖:
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(≤105)——整個師門的總人數(於是每個人從0到N−1編號,祖師爺的編號為0);Z——祖師爺的功力值(不一定是整數,但起碼是正數);r——每傳一代功夫所打的折扣百分比值(不超過100的正數)。接下來有N行,第i行(i=0,⋯,N−1)描述編號為i的人所傳的徒弟,格式為:
KiID[1] ID[2]⋯ID[Ki]
其中Ki是徒弟的個數,後面跟的是各位徒弟的編號,數字間以空格間隔。Ki為零表示這是一位得道者,這時後面跟的一個數字表示其武功被放大的倍數。
輸出格式:
在一行中輸出所有得道者的功力總值,只保留其整數部分。題目保證輸入和正確的輸出都不超過1010。
輸入樣例:
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;
}