强连通分量一共有三种算法Kosaraju、Tarjan、Gabow算法,本人目前只会Tarjan,所以就简单发一下以前所做过的强连通分量的题(当然都是比较简单的~ ~)。
首先是一道裸题。
舞会
题意就是求一个 图中强连通分量的个数,只要先把图建好,用到了伪链表,然后用一次Tarjan,就可以顺利求出个数,也不需要记录点属于哪一个强连通分量。
代码:
#include<cstdio> #include<cstring> using namespace std; const int maxn = 200 + 10; struct edge { int y; int n; }edges[5000]; int dfn[maxn],low[maxn]; int link[maxn]; int stop,step; int stack[maxn]; bool instack[maxn]; int n,m; int ans; void init() { freopen("tyvj1111.in","r",stdin); freopen("tyvj1111.out","w",stdout); } void make(int x,int y) { ++m; edges[m].y = y; edges[m].n = link[x]; link[x] = m; } void readdata() { scanf("%d",&n); for(int i = 1;i <= n;i++) { int t; scanf("%d",&t); while(t) { make(i,t); scanf("%d",&t); } } } void tarjan(int i) { dfn[i]= low[i] = ++step; instack[i] = true; stack[++stop] = i; int t = link[i]; while(t != 0) { int y = edges[t].y; if(!dfn[y]) { tarjan(y); if(low[y] < low[i]) low[i] = low[y]; } else if(instack[y] && dfn[y]<low[i]) { low[i] = dfn[y]; } t = edges[t].n; } int j; if(dfn[i] == low[i]) { ++ans; do { j = stack[stop--]; instack[j] = false; }while(j!=i); } } void solve() { ans = 0; stop = step = m = 0; memset(dfn,sizeof(dfn),0); for(int i = 1;i <= n;i++) { if(!dfn[i]) tarjan(i); } printf("%d",ans); } int main() { init(); readdata(); solve(); return 0; }
接下来:相连的农场
在前一道题中多了一个记录强连通分量中点的编号,记录好了按顺序来枚举输出就行了。
代码:
#include<cstdio> #include<cstring> using namespace std; const int maxn = 500 + 10; int low[maxn],dfn[maxn]; int n; int ans; int step,stop; int stack[maxn]; bool instack[maxn],mark[maxn]; int belong[maxn]; struct pnode { int d; pnode *next; }; pnode *link[maxn]; void init() { freopen("rqnoj480.in","r",stdin); freopen("rqnoj480.out","w",stdout); } void insert(int x,int y) { pnode *p = new pnode; p->d = y; p->next = link[x]; link[x] = p; } void readdata() { scanf("%d",&n); for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) { int t; scanf("%d",&t); if(t) { insert(i,j); } } } void tarjan(int i) { dfn[i] = low[i] = ++step; pnode *p = link[i]; stack[++stop] = i; instack[i] = true; while(p != NULL) { int y = p -> d; if(!dfn[y]) { tarjan(y); if(low[y] < low[i]) low[i] = low[y]; } else if(instack[y] && dfn[y] < low[i]) { low[i] = dfn[y]; } p = p -> next; } int j; if(dfn[i] == low[i]) { ++ans; int cou = 0; do { j = stack[stop--]; instack[j] = false; belong[j] = ans; }while(i != j); } } void writeans() { printf("%d\n",ans); for (int i = 1; i < n + 1; ++i) { if (mark[belong[i]]) continue; mark[belong[i]] = true; printf("%d ",i); for (int j = i + 1; j < n + 1; ++j) if (belong[j] == belong[i]) printf("%d ",j); printf("\n"); } } void solve() { memset(belong,sizeof(belong),67); step = ans = stop = 0; for(int i = 1;i <= n;i++) { if(!dfn[i]) { tarjan(i); } } writeans(); } int main() { init(); readdata(); solve(); return 0; }然后就是一道难度更大的题了。 间谍网络
这道题算是我写过的比较长的程序了。首先建图,维护出哪个间谍能够被收买以及收买价格。然后做一次Tarjan,找出每一个强连通分量中能够收买的间谍。接着缩点,建一个新图,找新图中入度为0的点,然后枚举,将新图中入度为0的点全部收买,就能够控制所有间谍了。如果不能控制所有间谍,那就找到第一个不能控制的间谍并输出就行了。
代码:
#include<cstdio> #include<cstring> using namespace std; const int maxn = 3000 + 10; const int inf = 0x7fffffff; int n,p; bool buy[maxn]; int price[maxn]; bool map[maxn][maxn]; int belong[maxn],dfn[maxn],low[maxn],stack[maxn]; bool instack[maxn]; int cou,stop,step; int id[maxn]; bool flag1; void init() { freopen("tyvj1153.in","r",stdin); freopen("tyvj1153.out","w",stdout); } void readdata() { memset(buy,sizeof(buy),false); memset(map,sizeof(map),false); scanf("%d",&n); scanf("%d",&p); for(int i = 1;i <= p;i++) { int t,pri; scanf("%d%d",&t,&pri); buy[t] = true; price[t] = pri; } int r; scanf("%d",&r); for(int i = 1;i <= r;i++) { int x,y; scanf("%d%d",&x,&y); map[x][y] = true; } } int tarjan(int x) { low[x] = dfn[x] = ++step; instack[x] = true; stack[++stop] = x; for(int i = 1;i <= n;i++) { if(map[x][i]) { if(!dfn[i]) { tarjan(i); if(low[i] < low[x]) low[x] = low[i]; } else if(instack[i] && dfn[i] < low[x]) low[x] = dfn[i]; } } if(dfn[x] == low[x]) { int j; ++cou; do { j = stack[stop--]; instack[j] = false; belong[j] = cou; }while(x != j); } } void newmap() { for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) { if(map[i][j] && belong[i] != belong[j]) { ++id[belong[j]]; } } } int cal() { int ans1 = 0; int ans2 = 0; bool flag = false; for(int i = 1;i <= cou;i++) { flag = false; int min = inf; if(id[i] == 0) { for(int j = 1;j <= n;j++) { if(belong[j] == i && buy[j]) { flag = true; if(price[j] < min) min = price[j]; } } ans1 += min; if(!flag)break; } } if(!flag) { for(int i = 1;i <= n;i++) { int fig = belong[i]; int flag2 = false; for(int j = 1;j <= n;j++) { if(belong[j] == fig && buy[j]) { flag2 = true; break; } } if(!flag2) { ans2 = i; break; } } } if(flag) { flag1 = true; return ans1; } else { flag1 = false; return ans2; } } void solve() { cou = stop = step = 0; for(int i = 1;i <= n;i++) { if(!dfn[i]) tarjan(i); } newmap(); int ans = cal(); if(flag1) { printf("YES\n"); printf("%d",ans); } else { printf("NO\n"); printf("%d",ans); } } int main() { init(); readdata(); solve(); return 0; }