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

la 4287 Proving Equivalences

2018-02-21 14:56 260 查看
题目:

大意:
比如如果存在关系A-->B,B-->C,C-->D,D-->A,就可以说ABCD四个量相等。
现在有n个量,m个形如X-->Y的关系,求最少还需要添加几个这样的关系,使得n个量相等。
(前面讲的一堆讲矩阵的话和本题无关)

思路:
对于每一个X-->Y的条件在X和Y之间连一条有向边。
可以从题目中看出,当且仅当n个点在同一个强连通块中时,n个量相等。
所以问题就转化成了在图中最少需要添加几条有向边使得全图强连通,根据强连通图的定义,这张图中一定不存在入度或出度为0的点。
由于强连通分量中的点入度和出度一定不为0,所以先用tarjan缩点,此时图变成了有向无环图。
这时要把所有出度为0的点上加一条出边,把所有入度为0的点上加一条入边,最小的情况就是尽量从出度为0的点引一条有向边到入度为0的点。
所以最后的结果就是入度为0的点和出度为0的点的数量的最大值。

注意:
1、由于n的范围是20000,所以不能用二维数组。
2、图中仅有一个强连通分量时,要特判。

代码:
#include<bits/stdc++.h>
using namespace std;

#define maxn 20000
#define maxm 50000

int n,m;

vector<int> a[maxn+5];

int pre[maxn+5],low[maxn+5];
int clk;

int scc[maxn+5];
int cnt;

stack<int> s;

int in0[maxn+5],out0[maxn+5];

void init() {
for(int i=1; i<=maxn; i++) a[i].clear();
memset(pre,0,sizeof(pre));
memset(scc,0,sizeof(scc));
clk=cnt=0;
stack<int> emp;
s=emp;
}

void readin() {
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y);
}
}

void tj(int u) {
pre[u]=low[u]=++clk;
s.push(u);

for(int i=0; i<a[u].size(); i++) {
int v=a[u][i];
if(!pre[v]) {
tj(v);
low[u]=min(low[v],low[u]);
} else if(!scc[v]) {
low[u]=min(pre[v],low[u]);
}
}

if(low[u]==pre[u]) {
int x;
cnt++;
do {
x=s.top(),s.pop();
scc[x]=cnt;
} while(x!=u);
}
}

void print() {
for(int i=1; i<=cnt; i++) in0[i]=out0[i]=1;
for(int i=1; i<=n; i++ ) {
for(int j=0; j<a[i].size(); j++) {
int u=scc[i],v=scc[a[i][j]];
if(u!=v) in0[v]=0,out0[u]=0;
}
}
int s1=0,s2=0;
for(int i=1; i<=cnt; i++) {
if(in0[i]) s1++;
if(out0[i]) s2++;
}
if(cnt==1) printf("0\n");
else printf("%d\n",max(s1,s2));
}

int main() {
int T;
scanf("%d",&T);

while(T--) {
init();
readin();
for(int i=1; i<=n; i++) if(!pre[i]) tj(i);
print();
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息