您的位置:首页 > 编程语言 > Go语言

POJ2762 Going from u to v or from v to u

2016-05-09 19:56 351 查看
一.原题链接:http://poj.org/problem?id=2762

二.题目大意:给你一个有向图,问能不能在图中任意选2个点u, v,使得可以从u到v或者从v到u。

三.思路:

1.预备知识:

(1)在一个强连通分量里面,任意两点都有双向的路径。这是强连通分量的定义。

(2)求一个图强连通分量方法:

tarjan算法:详情:http://blog.csdn.net/h992109898/article/details/51318135

这题跟里面的tarjan算法不一样,不过差不多,也是利用dfn[]和low[]数组,用来求强连通分量。读完上面的博客,大概了解了dfn[]与low[]数组,这题dfn[]是用来标记搜索顺序,low[]用来标记可以到达的最高层祖先处。

求强连通分量的步骤是:每次进入DFS,直接将当前点进栈,然后扫所有的邻接点,有向图不用考虑是不是刚进来的边(跟回边混淆),没有访问过的,继续搜索,退出来之后取low[u] = min(low[u], low[v]),u为当前节点,v为邻接点。访问过的,说明一定有回边,low[u] = min(low[u], dfn[v])。扫完所有的边之后,如果dfn[u] == low[u],说明以u为根节点的深度优先搜索子树上的结点构成了一个强连通分量,把所有点出栈,缩点(待会再说)。为什么会这样呢?因为u肯定是可以到达子树上的所有结点,因为u是根部,而且每个子结点都可以到达u,因为每个子节点通过若干个回边,能回到u。假设没有结点到达u,那么子结点所回到的祖先结点,会自己变成一颗深度优先搜索树的根部,然后自己先弹栈。注意这里在DFS的时候,不能DFS(u,
depth+1),而一定要设一个变量,每次进入DFS就加1,这2者不一样的,因为前者会出现多个dfn[u]的值相同,会产生混淆。(反正我改完就AC了)。然后还是不知道为什么会这样。

2.思路切入:我们可以把每个强连通分量看成一个点,点内中随便找2个点肯定可以互通的(强连通分量定义)。然后通过桥构成一个新图,此时要判断的就是这个新图上任取2点u, v,能不能从u到达v或者从v到达u,记住是或者不是并且。然后其实只要进行一次拓扑排序,拓扑排序过程中如果出现入度为0的点同时有1个以上(不包括一个),则选这2个点就不满足条件,如果这2个点代表2个连通分量,那么选它们之中分别的任意2点都不满足条件,假设它们之间有路径,路径只有一个中间节点t,那么要使得2个点入度同时为0,那么必须先把t删除,因为要把t删除了,t连向2个点之中一个点的出边才会删除,要删除t必须删除另外一个节点,因为这样t的入度才为0,于是矛盾了。用数学归纳法可证明路径有多个节点的情况。

为什么不一开始就拓扑排序,而是要先找连通分量缩点呢?难道强连通分量可以拓扑排序?找得到入度为0的点?

四.代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <stack>

using namespace std;

const int MAX_N = 1558,
INF = 0x3f3f3f3f;

int nodeNum, edgeNum, cntId, visNum,
id[MAX_N], low[MAX_N], dfn[MAX_N];
vector <int> edge[MAX_N], tree[MAX_N];
bool visited[MAX_N], inStack[MAX_N];
stack <int> st;

void init()
{
int i;
visNum = 0;
for(i = 0; i < 1024; i++)
edge[i].clear(), tree[i].clear();
memset(visited, 0, sizeof(visited));
memset(inStack, 0, sizeof(inStack));
memset(tree, 0, sizeof(tree));
while(!st.empty())
st.pop();
cntId = 0;
}

void dfs(int u)
{
4000

visited[u] = true;
low[u] = dfn[u] = visNum++;
st.push(u);
inStack[u] = true;
int i, v;
for(i = 0; i < edge[u].size(); i++){
v = edge[u][i];
if(!visited[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(inStack[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u] && inStack[u]){
int tmp;
do{
tmp = st.top();
st.pop();
inStack[tmp] = false;
id[tmp] = cntId;
}while(tmp != u);
cntId++;
}
}

bool topoSort()
{
int u, i, v, k, cnt[MAX_N], j, cntIn, save;
bool mark[MAX_N];
memset(cnt, 0, sizeof(cnt));
memset(mark, 0, sizeof(mark));
for(i = 0; i < cntId; i++)
for(j = 0; j < tree[i].size(); j++)
cnt[tree[i][j]]++;

cntIn = 0;
for(i = 0; i < cntId; i++)
if(0 == cnt[i]){
cntIn++;
save = i;
}
if(cntIn > 1)
return false;
for(k = 0; k < cntId; k++){
cntIn = 0;
u = save;
for(i = 0; i < tree[u].size(); i++){
v = tree[u][i];
cnt[v]--;
if(0 == cnt[v]){
cntIn++;
save = v;
}
}
if(cntIn > 1)
return false;
}

return true;
}

bool judge()
{
int i, j, v;
for(i = 1; i <= nodeNum; i++)
if(!visited[i])
dfs(i);

for(i = 1; i <= nodeNum; i++){
for(j = 0; j < edge[i].size(); j++){
v = edge[i][j];
if(id[i] != id[v])
tree[id[i]].push_back(id[v]);
}
}

return topoSort();
}

int main()
{
//freopen("in.txt", "r", stdin);

int test, i, j, u, v;
scanf("%d", &test);
while(test--){
init();
scanf("%d%d", &nodeNum, &edgeNum);
for(i = 0; i < edgeNum; i++){
scanf("%d%d", &u, &v);
edge[u].push_back(v);
}
if(judge())
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: