给出n个命题,m个推导,问最少增加多少条推导,可以使所有命题都能等价(两两都能互推)
既给出有向图,最少加多少边,使得原图变成强连通。
首先强连通缩点,对于新图,每个点都至少要有一条出去的边和一条进来的边(这样才能保证它能到任意点和任意点都能到它)
所以求出新图中入度为0的个数,和出度为0的个数,添加的边就是从出度为0的指向入度为0的。这样还会有一点剩余,剩余的就乱连就行了。
所以只要求出2者的最大值就OK。
#include <iostream> #include<cstring> #include<cstdio> #include<string> #include<algorithm> using namespace std; #define MAXN 30005 #define MAXM 200005 struct node { int to,next; }edge[MAXM]; int head[MAXN],en; int low[MAXN],dfn[MAXN],stack[MAXN],top,set[MAXN],col,num; bool vis[MAXN],instack[MAXN]; int in[MAXN],out[MAXN]; int n; int m; void addedge(int a,int b) { edge[en].to=b; edge[en].next=head[a]; head[a]=en++; } void tarjan(int u) { vis[u]=1; dfn[u]=low[u]=++num; instack[u]=true; stack[++top]=u; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(!vis[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v]) low[u]=min(dfn[v],low[u]); } if(dfn[u]==low[u]) { int j; col++; do { j=stack[top--]; instack[j]=false; set[j]=col; } while (j!=u); } } void init() { en=top=col=num=0; memset(head,-1,sizeof(head)); memset(instack,0,sizeof(instack)); memset(vis,0,sizeof(vis)); memset(set,-1,sizeof(set)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); } int main() { int a,b; int cas; scanf("%d",&cas); while(cas--) { scanf("%d%d",&n,&m); init(); for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); addedge(a,b); } for(int i=1;i<=n;i++) if(!vis[i])tarjan(i); if(col<=1) {puts("0");continue;} int ans=0; for(int i=1;i<=n;i++) for(int j=head[i];~j;j=edge[j].next) { int to=edge[j].to; if(set[to]!=set[i]) { in[set[to]]++; out[set[i]]++; } } int t1=0,t2=0; for(int i=1;i<=col;i++) { if(!in[i]) t1++; if(!out[i]) t2++; } printf("%d\n",max(t1,t2)); } return 0; } /* 3 3 1 2 2 1 1 2 */
hdu 2767 Proving Equivalences 强连通缩点
时间: 2024-10-24 16:02:55