题干:
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 1
Sample Output
Yes
题目大意:
问一个图是否是单连通的。
解题报告:
先对全图求一次SCC,可以知道每个SCC内的点都是单连通的,那么把每个SCC缩点构建出DAG之后再判断这个DAG是否单连通即可。方法不唯一:可以是拓扑排序,看他每次是否只有一个活跃点就可以了,如果不是一个,那肯定不符合要求。
第二种方法:是DAG动规找出最长链,如果最长链上的点个数等于SCC个数,那么DAG单连通。(因为如果最长链都不能覆盖所有点,那么必定有两个点之间是无法到达的)
刚开始以为直接判断是否是一条链就行了,后来3 -> 1、 1 -> 2、3 -> 2这组数据就Hack掉了,,本应该是Yes,但是这样肯定就是No了。然后想判一个欧拉通路,虽然可以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];
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
这组样例就过不了,所以不能简单判断一个有向图欧拉通路。