【POJ - 2762】Going from u to v or from v to u?(Tarjan缩点,树形dp 或 拓扑排序,欧拉图相关)
題干:
In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?
Input
The first line contains a single integer T, the number of test cases. And followed T cases.?
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.?
Output
The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.
Sample Input
1 3 3 1 2 2 3 3 1Sample Output
Yes題目大意:
問一個圖是否是單連通的。
解題報告:
先對全圖求一次SCC,可以知道每個SCC內(nèi)的點都是單連通的,那么把每個SCC縮點構(gòu)建出DAG之后再判斷這個DAG是否單連通即可。方法不唯一:可以是拓撲排序,看他每次是否只有一個活躍點就可以了,如果不是一個,那肯定不符合要求。
第二種方法:是DAG動規(guī)找出最長鏈,如果最長鏈上的點個數(shù)等于SCC個數(shù),那么DAG單連通。(因為如果最長鏈都不能覆蓋所有點,那么必定有兩個點之間是無法到達的)
?
剛開始以為直接判斷是否是一條鏈就行了,后來3 -> 1、?1 -> 2、3?-> 2這組數(shù)據(jù)就Hack掉了,,本應(yīng)該是Yes,但是這樣肯定就是No了。然后想判一個歐拉通路,雖然可以AC,但是有數(shù)據(jù)是過不了的。(數(shù)據(jù)水了)
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 1000 + 5; struct Edge {int fr,to,ne; } e[6005],E[6005]; vector<int> vv[MAX]; int head[MAX],HEAD[MAX],tot,TOT; bool ok[MAX][MAX]; void add(int u,int v) {e[++tot].to = v;e[tot].fr = u;e[tot].ne = head[u];head[u] = tot; } int n,m; int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; void Tarjan(int x) {DFN[x] = LOW[x] = ++clk;vis[x]=1;sk[++index] = x;for(int i = head[x]; ~i; i = e[i].ne) {int v = e[i].to;if(DFN[v] == 0) {Tarjan(v);LOW[x] = min(LOW[x],LOW[v]);}else if(vis[v]) {LOW[x] = min(LOW[x],DFN[v]);} }if(LOW[x] == DFN[x]) {scc++;while(1) {int tmp = sk[index];index--;vis[tmp]=0;col[tmp] = scc;if(tmp == x) break;} } } void init() {scc=clk=tot=TOT=index=0;for(int i = 1; i<=n; i++) {vv[i].clear();head[i] = HEAD[i] = -1;vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = 0;}for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) ok[i][j]=0;} } bool topu() {queue<int> q;for(int i = 1; i<=scc; i++) {if(in[i] == 0) q.push(i);}int cnt = 0;while(!q.empty()) {if(q.size() > 1) return 0 ;int cur = q.front();q.pop();cnt++;int up = vv[cur].size();for(int i = 0; i<up; i++) {int v = vv[cur][i];in[v]--;if(in[v] == 0) q.push(v);} }return cnt == scc; } int main() {int t;cin>>t;while(t--) {scanf("%d%d",&n,&m); init();for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b);}for(int i = 1; i<=n; i++) {if(DFN[i] == 0) Tarjan(i);}for(int a,b,i = 1; i<=tot; i++) {a=col[e[i].fr],b=col[e[i].to];if(a==b) continue;if(ok[a][b]) continue;ok[a][b]=1;in[b]++;out[a]++;vv[a].pb(b);}bool flag = topu();if(flag) puts("Yes");else puts("No");}return 0 ; }AC代碼:(樹形dp)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 1000 + 5; struct Edge {int fr,to,ne; } e[6005],E[6005]; vector<int> vv[MAX]; int head[MAX],HEAD[MAX],tot,TOT; bool ok[MAX][MAX]; void add(int u,int v) {e[++tot].to = v;e[tot].fr = u;e[tot].ne = head[u];head[u] = tot; } int n,m; int dp[MAX]; int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; void Tarjan(int x) {DFN[x] = LOW[x] = ++clk;vis[x]=1;sk[++index] = x;for(int i = head[x]; ~i; i = e[i].ne) {int v = e[i].to;if(DFN[v] == 0) {Tarjan(v);LOW[x] = min(LOW[x],LOW[v]);}else if(vis[v]) {LOW[x] = min(LOW[x],DFN[v]);} }if(LOW[x] == DFN[x]) {scc++;while(1) {int tmp = sk[index];index--;vis[tmp]=0;col[tmp] = scc;if(tmp == x) break;} } } void init() {scc=clk=tot=TOT=index=0;for(int i = 1; i<=n; i++) {vv[i].clear();head[i] = HEAD[i] = -1;vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = dp[i] = 0;}for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) ok[i][j]=0;} } int dfs(int cur){if(dp[cur])return dp[cur];int num=0;int up = vv[cur].size();for(int i = 0; i<up; i++) {int v = vv[cur][i];num=max(num,dfs(v));}return dp[cur]=num+1; } int main() {int t;cin>>t;while(t--) {scanf("%d%d",&n,&m); init();for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b);}for(int i = 1; i<=n; i++) {if(DFN[i] == 0) Tarjan(i);}for(int a,b,i = 1; i<=tot; i++) {a=col[e[i].fr],b=col[e[i].to];if(a==b) continue;if(ok[a][b]) continue;ok[a][b]=1;in[b]++;out[a]++;vv[a].pb(b);}int flag = 0;for(int i = 1; i<=n; i++){if(dfs(LOW[i]) == scc){flag=true;break;}//或者dfs(i)也可以ac}if(flag) puts("Yes");else puts("No");}return 0 ; }這里送上一種雖然錯誤但是可以AC的代碼,引以為戒。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define F first #define S second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 1000 + 5; struct Edge {int fr,to,ne; } e[6005],E[6005]; int f[MAX]; int getf(int v) {return f[v] == v ? v : f[v] = getf(f[v]); } void merge(int u,int v) {int t1 = getf(u),t2 = getf(v);f[t2] = t1; } int head[MAX],HEAD[MAX],tot,TOT; bool ok[MAX][MAX]; void add(int u,int v) {e[++tot].to = v;e[tot].fr = u;e[tot].ne = head[u];head[u] = tot; } int n,m; int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; void Tarjan(int x) {DFN[x] = LOW[x] = ++clk;vis[x]=1;sk[++index] = x;for(int i = head[x]; ~i; i = e[i].ne) {int v = e[i].to;if(DFN[v] == 0) {Tarjan(v);LOW[x] = min(LOW[x],LOW[v]);}else if(vis[v]) {LOW[x] = min(LOW[x],DFN[v]);} }if(LOW[x] == DFN[x]) {scc++;while(1) {int tmp = sk[index];index--;vis[tmp]=0;col[tmp] = scc;if(tmp == x) break;} } } void init() {scc=clk=tot=TOT=index=0;for(int i = 1; i<=n; i++) {head[i] = HEAD[i] = -1;f[i]=i;vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = 0;}for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) ok[i][j]=0;} } int main() {int t;cin>>t;while(t--) {scanf("%d%d",&n,&m); init();for(int a,b,i = 1; i<=m; i++) {scanf("%d%d",&a,&b);add(a,b);}for(int i = 1; i<=n; i++) {if(DFN[i] == 0) Tarjan(i);}for(int a,b,i = 1; i<=tot; i++) {a=col[e[i].fr],b=col[e[i].to];if(a==b) continue;if(ok[a][b]) continue;ok[a][b]=1;merge(a,b);in[b]++;out[a]++;}int ans = 0;int flag = 0,ru=0,chu=0;for(int i = 1; i<=scc; i++) {if(f[i] == i) ans++;if(in[i]==out[i]) continue;if(in[i]-out[i]==1) ru++;else if(out[i]-in[i]==1) chu++;else {flag=1; break;}} if(ans > 1 || flag == 1 || ru!=chu || ru>1 || chu>1 ) puts("No");else puts("Yes");}return 0 ; }但是其實這樣是不對的:
1
3 3
3 1
1 2
3 2
這組樣例就過不了,所以不能簡單判斷一個有向圖歐拉通路。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的【POJ - 2762】Going from u to v or from v to u?(Tarjan缩点,树形dp 或 拓扑排序,欧拉图相关)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《赛博朋克2077》各载具坐标秘籍分享
- 下一篇: 【CodeForces - 768C】J