2011ACM上海邀请赛I题(Imitation)----强连通缩点+DFS+求传递闭包

来源:互联网 发布:淘宝货源都在哪里进货 编辑:程序博客网 时间:2024/04/29 06:54

PS.上海邀请赛唯一一道图论题,当时做的人不多,就没去看。赛后想了下思路,觉得能做,可是一直WA,找了半天bug才发现。所以把那些比较会卡的数据也贴出来了、自己改完bug后的代码复杂度有点高,随机生成了10000组数据等了半天才出来答案,以为可能要TLE了,一交居然过了,而且比我慢的还有,可能数据小,不卡时间吧、、、

 

 

思路:

强连通缩点+DFS+求传递闭包

(1):要去掉自环
(2):要去重边(原图和新图都要去重)
(3):对缩点后的图深搜时要注意(1.要从每个点开始搜;2.每次更新dis后还要继续往下搜)

CODE:

/*AC代码:336ms*/#include <iostream>#include <cstdio>#include <memory.h>#include <algorithm>#define MAXN 1005//N#define MAXM 10005//M#define min(a,b) (a<b?a:b)#define max(a,b) (a>b?a:b)using namespace std;struct edge{int u,v,next;bool ok;//判断这条边是不是在最基础的图上}E[MAXM],sE[MAXM];int head[MAXN],ecnt;int shead[MAXN],secnt;//缩点后重新构图int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];int Index,scc,top,cas,N,M;int num[MAXN],dis[MAXN],pre[MAXM];bool Instack[MAXN];bool map[MAXN][MAXN];bool vis[MAXN];int ans_Max,ans_Min;void Insert(int u,int v){E[ecnt].u=u;E[ecnt].v=v;E[ecnt].next=head[u];head[u]=ecnt++;}void sInsert(int u,int v)//w表示重边数{sE[secnt].u=u;sE[secnt].v=v;sE[secnt].ok=false;sE[secnt].next=shead[u];shead[u]=secnt++;}void Tarjan(int u){int i,v;Low[u]=DFN[u]=++Index;Stack[++top]=u;Instack[u]=true;for(i=head[u];i!=-1;i=E[i].next){v=E[i].v;if(!DFN[v]){Tarjan(v);if(Low[u]>Low[v])Low[u]=Low[v];}else if(Instack[v]&&Low[u]>DFN[v])Low[u]=DFN[v];}if(Low[u]==DFN[u]){scc++;do{v=Stack[top--];Instack[v]=false;Belong[v]=scc;num[scc]++;}while(u!=v);}return;}void Init(){int i,j,u,v;scanf("%d%d",&N,&M);memset(head,-1,sizeof(head));ecnt=0;//原图memset(shead,-1,sizeof(shead));secnt=0;memset(map,false,sizeof(map));ans_Max=ans_Min=0;for(i=1;i<=M;i++){scanf("%d%d",&u,&v);map[u][v]=true;}//去重边for(i=1;i<=N;i++){for(j=1;j<=N;j++){if(i!=j&&map[i][j])Insert(i,j);}}}void Shrink()//缩点+重新构图{int i,j,u,v;memset(num,0,sizeof(num));memset(DFN,0,sizeof(DFN));memset(Instack,false,sizeof(Instack));memset(Low,0,sizeof(Low));Index=scc=top=0;for(i=1;i<=N;i++){if(!DFN[i]) Tarjan(i);}for(i=1;i<=scc;i++){if(num[i]!=1)ans_Min+=num[i];}//重新构图memset(map,false,sizeof(map));for(i=0;i<ecnt;i++){u=E[i].u;v=E[i].v;if(Belong[u]!=Belong[v])map[Belong[u]][Belong[v]]=true;}for(i=1;i<=scc;i++){for(j=1;j<=scc;j++){if(j!=i&&map[i][j])sInsert(i,j);}  }}void dfs(int u){int i,v;for(i=head[u];i!=-1;i=E[i].next){v=E[i].v;if(!vis[v]){vis[v]=1;dfs(v);} }}int Closuer()//在原图上求传递闭包{int i,j,sum=0;for(i=1;i<=N;i++){memset(vis,false,sizeof(vis));vis[i]=true;dfs(i);for(j=1;j<=N;j++){if(vis[j]) sum++;}sum--;}return sum;}void dfs2(int u,int len){int i,v;for(i=shead[u];i!=-1;i=sE[i].next){v=sE[i].v;if(dis[v]==-1){dis[v]=len;pre[v]=i;dfs2(v,len+1);}else{if(len>dis[v]){dis[v]=len;pre[v]=i;dfs2(v,len+1);//注意还要往下搜}}}}int Run()//计算缩点后的图中基本的边个数{int i,j,res=0;for(i=1;i<=scc;i++)//从每个入度为零的点开始深搜{memset(dis,-1,sizeof(dis));memset(pre,-1,sizeof(pre));dis[i]=0;dfs2(i,1);for(j=1;j<=scc;j++){if(pre[j]!=-1)sE[pre[j]].ok=true;}}for(i=0;i<secnt;i++){if(sE[i].ok)res++;}return res;}void Solve(){ans_Max=Closuer();Shrink();ans_Min+=Run();printf("Case #%d: %d %d\n",cas++,ans_Min,ans_Max);}int main(){//char s[100];//freopen("I.in","r",stdin);//freopen("I(2).out","w",stdout);int T;cas=1;scanf("%d",&T);while(T--){//scanf("%s",s);Init();Solve();}return 0;}/*14 202 19 113 1413 67 149 103 114 83 911 47 613 119 1012 612 213 96 37 511 98 6Case #1: 15 487 111 21 31 51 72 33 44 24 55 66 77 5Case #2: 8 2718 193 42 716 179 813 33 166 137 173 145 418 143 1313 216 914 710 58 1018 16 18Case #3: 18 6111 246 62 65 119 78 411 39 31 32 511 710 22 85 53 17 711 710 26 76 32 38 55 46 37 1Case #4: 12 419 144 52 97 43 62 72 53 14 98 63 86 48 57 61 2Case #5: 9 329 102 72 53 14 98 63 86 48 57 61 2Case #6: 10 299 92 53 14 98 63 86 48 57 61 2Case #7: 9 209 91 23 16 49 62 49 39 44 82 8Case #8: 7 18*/