您的位置:首页 > 产品设计 > UI/UE

UVALive 4287 Proving Equivalences(SCC、缩点+图的连通性)

2014-02-24 15:01 435 查看
题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2288

题目大意:有n个命题,先要证明他们全部等价,你的朋友已经做了m步等价关系的证明,让你算至少需要几部才能完成整个等价性的证明。

解题思路:比较基础的一道题吧。整个题目相当于给你m条单向边,问你最少补上几条边使得整个图强连通。很容易想到先缩点,整个图就是DAG了,然后对缩点后的SCC图,算出每个SCC团的入度和出度,那么要使整个图强连通,每个点的入度和出度都至少为1,所以答案就是max(入度为0的点个数,出度为0的点个数)。然后还有最重要的一点,也是这道题最恶心人的地方,那就是如果整个图先开始就是强连通的,那么答案就是0,不是1,要特判

代码如下:

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;

const int MAXN = 22222;

vector <int> G[MAXN];
int pre[MAXN],low[MAXN],sccno[MAXN];
int dfs_clock,scc_cnt;

stack <int> S;

void dfs(int u)
{
pre[u] = low[u] = ++dfs_clock;
S.push(u);
for(int i = 0;i < G[u].size();i++)
{
int v = G[u][i];
if(!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccno[v])
low[u] = min(low[u],pre[v]);
}
if(low[u] == pre[u])
{
scc_cnt++;
for(;;)
{
int x = S.top();
S.pop();
sccno[x] = scc_cnt;
if(x == u) break;
}
}
}

void find_scc(int n)
{
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
dfs_clock = scc_cnt = 0;
for(int i = 0;i < n;i++)
if(!pre[i]) dfs(i);
}

int in[MAXN],out[MAXN];

int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++)
G[i].clear();
for(int i = 0;i < m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
a--;
b--;
G[a].push_back(b);
}

find_scc(n);

memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int u = 0;u < n;u++)
for(int i = 0;i < G[u].size();i++)
{
int v = G[u][i];
if(sccno[u] != sccno[v])
{
out[sccno[u]]++;
in[sccno[v]]++;
}
}
int ans1 = 0,ans2 = 0;
for(int i = 1;i <= scc_cnt;i++)
{
if(in[i] == 0) ans1++;
if(out[i] == 0) ans2++;
}
int ans = max(ans1,ans2);
if(scc_cnt == 1) ans = 0;//特判,此题最阴险的地方!
printf("%d\n",ans);
}
return 0;
}

/*
2 2
1 2
2 1
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: