?? usaco_5_3_3_schlnet-強連通分量.cpp
字號:
/*
ID: wangyuc2
PROG: schlnet
LANG: C++
*/
/*
這是一道收縮強連通分量的題。
該題描述的是一個有向圖。我們都知道,在一個有向圖強連通分量中從任意一個頂點開始,可以到達強連通分量的每個頂點。由此可以把該題中所有強連通分量收縮成分別一個頂點,則入度為0的頂點就是最少要接受新軟件副本的學校。
第二問就是,問至少添加多少條邊,才能使原圖強連通。也就問在收縮后的圖至少添加多少條邊,才能使之強連通。
可以知道,當存在一個頂點入度為0或者出度為0的時候,該圖一定不是強連通的。為了使添加的邊最少,則應該把入度為0頂點和出度為0的頂點每個頂點添加1條邊,使圖中不存在入度為0頂點和出度為0的頂點。
當入度為0的頂點多于出度為0的頂點,則應添加的邊數應為入度為0的頂點的個數。當出度為0的頂點多于出入度為0的頂點,則應添加的邊數應為出度為0的頂點的個數。
這樣就可以解決問題了。但是不要忘了還有特殊的情況,當原圖本身就是強連通分量時,收縮成一個頂點,該頂點入度和出度都為0,但第一問應為1,第二問應為0。
求強連通分量,我用的兩遍深搜的Kosaraju算法,時間復雜度為O(n)。把找到的每個強連通分量收縮為一的頂點,組成新圖。設r(x)為x所在的強連同分量的代表節點,如果原圖中存在邊e(x,y),那么新圖中有邊e(r(x),r(y)) 。然后根據點的鄰接關系統計出度和入度即可。
[編輯] 求強連通分量的另一種方法
Sinya覺得寫深搜求太麻煩了,所以Sinya就用了另一種求強連通分量的方法。
用froyed算出兩點間是否可以互相到達。然后枚舉任意兩個頂點Vi還有Vj,如果兩個點互相可以到達,那么他們就是屬于同一個強連通分量了。
雖然是O(n^3)的算法??墒沁@道題能過。可以大幅降低編程復雜度。
但是Sinya又覺得我們要精益求精,所以大家也要掌握深搜法哦
[編輯] 旁門左道
本題我用的是一種非標準算法。不知道為什么對,但是過了。
第一問是求最小點基。這個我是分步驟計算的:
首先,所有入度為0的點肯定要得到軟件,因為如果得不到,那么沒有別的點來通過網絡傳輸給它。找出所有入度為0的點,把這些點以及他所能到達的點全都作上標記。
對于剩下的點,找出塊的個數,這里定義兩個點連通當且僅當兩個點之間存在路徑,不考慮方向。原因很簡單,兩個點之間只要其中一個能到達另一個即可,這樣的塊中必然有一個點可以作為起點,而由于前一步已經把入度為0的點去掉了,所以這樣的塊一定從起點可以到達所有點。
第一問的答案就是上述兩者個數之和。
第二問首先統計整個圖的入度為0和出度為0的點的個數,前者再加上上一步求出來的塊的個數(當整個圖就是一個塊的時候不用加),答案就是兩者中的較大者。
*/
/*
Kosaraju算法
來自"NOCOW"
跳轉到: 導航, 搜索
/*這是一個求圖的強連通分量的算法。*/
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
#define NMAX 11000
vector< vector< int > > path;
vector< vector< int > > npath;
int n,m, scc;
int order[NMAX], order_pos, id[NMAX], id_total[NMAX];
bool vis[NMAX];
int out_degre[NMAX];
void dfs(int pos)
{
int i,j,l;
vis[pos] = true;
l = path[pos].size();
for (i=0;i<l;i++) {
j = path[pos][i];
if (!vis[j]) {
dfs(j);
}
}
order[ order_pos ++ ] = pos;//make order
}
void ndfs(int pos)
{
int i,j,l;
vis[pos] = true;
id[pos] = scc;
l = npath[pos].size();
for (i=0;i<l;i++) {
j = npath[pos][i];
if (!vis[j]) {
ndfs(j);
}
}
}
void Kosaraju()
{
int i,j,l;
//dfs in original graph
memset(vis, 0, sizeof(vis));
for (i=1; i<=n ;i++) {
if (!vis[i]) {
dfs(i);
}
}
//dfs in inverse graph
memset(vis, 0, sizeof(vis));
memset(id, 0, sizeof(id));
scc = 1;
for (i=order_pos-1; i>=0 ;i--) {
if (!vis[ order[i] ]) {
ndfs(order[i]);
scc ++;
}
}
//statist
scc --;
memset(id_total, 0, sizeof(id_total));
for (i=1;i<=n;i++) {
id_total[ id[i] ] ++;
}
memset(out_degre, 0, sizeof(out_degre));
for (i=1;i<=n;i++) {
l = path[i].size();
int id1 = id[i];
for (j=0;j<l;j++) {
int id2 = id[ path[i][j] ];
if (id1 != id2) {//id1 -> id2
out_degre[id1] ++;
}
}
}
int ans_id,zero_degre = 0;
for (i=1;i<=scc;i++) {
if (out_degre[i] == 0) {
zero_degre ++;
ans_id = i;
}
}
if (zero_degre > 1) {
printf("0\n");
}
else {
printf("%d\n",id_total[ ans_id ]);
}
}
int main()
{
int i,j;
while (scanf("%d %d",&n,&m)==2) {
path.resize(n+10);
npath.resize(n+10);
for (i=0;i<=n;i++) {
path[i].clear();
npath[i].clear();
}
order_pos = 0;
//set graph
for (i=0;i<m;i++) {
int x,y;
scanf("%d %d",&x,&y);
path[x].push_back(y);
npath[y].push_back(x);
}
Kosaraju();
}
}
*/
//written by CmYkRgB123
#include <iostream>
#include <fstream>
#define MAXN 101
#define max(x,y) ((x)>(y)?x:y)
using namespace std;
ifstream fi("schlnet.in");
ofstream fo("schlnet.out");
int N,M;
int adjl[MAXN][MAXN],fdjl[MAXN][MAXN];
bool vis[MAXN],dis[MAXN][MAXN];
int belong[MAXN],ind[MAXN],oud[MAXN],i0,o0;
void init()
{
int t,i;
fi >> N;
for (i=1;i<=N;i++)
{
fi >> t;
while (t)
{
adjl[i][ ++adjl[i][0] ]=t;
fdjl[t][ ++fdjl[t][0] ]=i;
fi >> t;
}
}
}
void dfs(int i)
{
int j,k;
vis[i]=true;
for (k=1;k<=adjl[i][0];k++)
{
j=adjl[i][k];
if (!vis[j])
dfs(j);
}
}
void fdfs(int i)
{
int j,k;
belong[i]=M;
for (k=1;k<=fdjl[i][0];k++)
{
j=fdjl[i][k];
if (vis[j] && !belong[j])
fdfs(j);
}
}
void solve()
{
int i,j,k;
for (i=1;i<=N;i++)
{
if (!belong[i])
{
dfs(i);
M++;
fdfs(i);
memset(vis,0,sizeof(vis));
}
}
for (i=1;i<=N;i++)
{
for (k=1;k<=adjl[i][0];k++)
{
j=adjl[i][k];
dis[belong[i]][belong[j]]=true;
}
}
for (i=1;i<=M;i++)
{
for (j=1;j<=M;j++)
{
if (i!=j && dis[i][j])
{
oud[i]++;
ind[j]++;
}
}
}
for (i=1;i<=M;i++)
{
if (ind[i]==0)
i0++;
if (oud[i]==0)
o0++;
}
}
void print()
{
if (M==1)
fo << 1 << endl << 0 << endl;
else
{
fo << i0 << endl;
fo << max(i0,o0) << endl;
}
fi.close();
fo.close();
}
int main()
{
init();
solve();
print();
return 0;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -