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 20 2 1 9 1 13 14 13 6 7 14 9 10 3 1 14 8 3 9 11 4 7 6 13 11 9 10 12 6 12 2 13 9 6 3 7 5 11 9 8 6 Case #1: 15 48 7 11 1 2 1 3 1 5 1 7 2 3 3 4 4 2 4 5 5 6 6 7 7 5 Case #2: 8 27 18 19 3 4 2 7 16 17 9 8 13 3 3 16 6 13 7 17 3 14 5 4 18 14 3 13 13 2 16 9 14 7 10 5 8 10 18 1 6 18 Case #3: 18 61 11 24 6 6 2 6 5 11 9 7 8 4 11 3 9 3 1 3 2 5 11 7 10 2 2 8 5 5 3 1 7 7 11 7 10 2 6 7 6 3 2 3 8 5 5 4 6 3 7 1 Case #4: 12 41 9 14 4 5 2 9 7 4 3 6 2 7 2 5 3 1 4 9 8 6 3 8 6 4 8 5 7 6 1 2 Case #5: 9 32 9 10 2 7 2 5 3 1 4 9 8 6 3 8 6 4 8 5 7 6 1 2 Case #6: 10 29 9 9 2 5 3 1 4 9 8 6 3 8 6 4 8 5 7 6 1 2 Case #7: 9 20 9 9 1 2 3 1 6 4 9 6 2 4 9 3 9 4 4 8 2 8 Case #8: 7 18 */