uvalive 3523 Knights of the Round Table 圆桌骑士(强连通+二分图)

题目真心分析不出来。看了白书才明白,不过有点绕脑。

容易想到,把题目给的不相邻的关系,利用矩阵,反过来建图。既然是全部可行的关系,那么就应该能画出含奇数个点的环。求环即是求双连通分量:找出所有的双连通分量,只要分量中的点数是奇数,则排除“must be expelled”的可能性。

判环上的点数用二分图,这个我都想了半天= =,如果是奇数个点,明摆着多出来的一个点放到哪个集合都会与集合内的点连边(这个找三个点自己画画试试就明白了)。0、1染色,本人喜欢用bfs,递归什么的其实都可以。

我自己用缩点做的,果断wa到吐。举个例子:五个点,{1,2,3}{3,4,5},这样3就是一个割顶了,缩点的话是在遍历完邻接表之后,再判断low[u]==dfn[u],如此5个点就缩成了一个点,即一个分量,虽然这个分量包含奇数个点,输出同样是0,但与我们的思路是不一样的。实际情况是分成两个分量,每个分量有三个点,割顶3同时出现在两个分量中。然后想着改进,当把割顶弹出栈后,再弹入,搞啊搞,还是算了,学着白书上用边搞。

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<vector>
  4 #include<stack>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int MAXN=1111;
  9 
 10 struct EDGE{
 11     int u,v;
 12     EDGE(){}
 13     EDGE(int _u,int _v):u(_u),v(_v){}
 14 };
 15 
 16 struct Edge{
 17     int v,next;
 18     Edge(){}
 19     Edge(int _v,int _next):v(_v),next(_next){}
 20 }edge[MAXN*MAXN];
 21 
 22 int mp[MAXN][MAXN],tol,head[MAXN];
 23 int low[MAXN],dfn[MAXN],bccno[MAXN],iscut[MAXN],TT,bcc_cnt;
 24 int que[MAXN],color[MAXN];
 25 int sign[MAXN];
 26 
 27 vector<int >bcc[MAXN];
 28 stack<EDGE >stk;
 29 
 30 void init()
 31 {
 32     tol=0;
 33     memset(head,-1,sizeof(head));
 34 }
 35 
 36 void add(int u,int v)
 37 {
 38     edge[tol]=Edge(v,head[u]);
 39     head[u]=tol++;
 40 }
 41 
 42 void dfs(int u,int fa)
 43 {
 44     low[u]=dfn[u]=++TT;
 45     int son=0;
 46     for(int i=head[u];i!=-1;i=edge[i].next)
 47     {
 48         int v=edge[i].v;
 49         EDGE e=EDGE(u,v);
 50         if(!dfn[v]){
 51             stk.push(e);
 52             son++;
 53             dfs(v,u);
 54             low[u]=min(low[v],low[u]);
 55             if(low[v]>=low[u]){
 56                 iscut[u]=1;
 57                 bcc_cnt++;
 58                 bcc[bcc_cnt].clear();
 59                 while(1)
 60                 {
 61                     EDGE x=stk.top();
 62                     stk.pop();
 63                     if(bccno[x.u]!=bcc_cnt){
 64                         bcc[bcc_cnt].push_back(x.u);
 65                         bccno[x.u]=bcc_cnt;
 66                     }
 67                     if(bccno[x.v]!=bcc_cnt){
 68                         bcc[bcc_cnt].push_back(x.v);
 69                         bccno[x.v]=bcc_cnt;
 70                     }
 71                     if(x.u==u&&x.v==v)
 72                         break;
 73                 }
 74             }
 75         }else if(dfn[v]<dfn[u]&&v!=fa){
 76             stk.push(e);
 77             low[u]=min(low[u],dfn[v]);
 78         }
 79     }
 80     if(fa<0&&son==1)
 81         iscut[u]=0;
 82 }
 83 
 84 void find_bcc(int n)
 85 {
 86     memset(low,0,sizeof(low));
 87     memset(dfn,0,sizeof(dfn));
 88     memset(bccno,0,sizeof(bccno));
 89     memset(iscut,0,sizeof(iscut));
 90 
 91     TT=bcc_cnt=0;
 92 
 93     for(int i=1;i<=n;i++)
 94         if(!dfn[i])
 95             dfs(i,-1);
 96 }
 97 
 98 bool bfs(int x,int fa)
 99 {
100     int l,r;
101     l=r=0;
102     que[r++]=x;
103     while(l<r)
104     {
105         int u=que[l++];
106         for(int i=head[u];i!=-1;i=edge[i].next)
107         {
108             int v=edge[i].v;
109             if(bccno[v]!=fa)
110                 continue ;
111             if(color[v]==-1){
112                 color[v]=color[u]^1;
113                 que[r++]=v;
114             }else if(color[v]==color[u])
115                 return false;
116         }
117     }
118     return true;
119 }
120 
121 void Bjudge()
122 {
123     memset(sign,0,sizeof(sign));
124     for(int i=1;i<=bcc_cnt;i++)
125     {
126         memset(color,-1,sizeof(color));
127         for(int j=0;j<bcc[i].size();j++)
128             bccno[bcc[i][j]]=i;
129         int u=bcc[i][0];
130         color[u]=0;
131         if(!bfs(u,i)){
132             for(int j=0;j<bcc[i].size();j++)
133                 sign[bcc[i][j]]=1;
134         }
135     }
136 }
137 
138 int main()
139 {
140     int n,m;
141     int a,b;
142     while(~scanf("%d%d",&n,&m)!=EOF)
143     {
144         if(!n&&!m)
145             break;
146         memset(mp,0,sizeof(mp));
147         for(int i=0;i<m;i++)
148         {
149             scanf("%d%d",&a,&b);
150             mp[a][b]=mp[b][a]=1;
151         }
152 
153         init();
154         for(int i=1;i<=n;i++)
155         {
156             for(int j=i+1;j<=n;j++)
157             {
158                 if(!mp[i][j]){
159                     add(i,j);
160                     add(j,i);
161                 }
162             }
163         }
164         
165         find_bcc(n);
166 
167         Bjudge();
168 
169         int ans=0;
170         for(int i=1;i<=n;i++)
171             if(!sign[i])
172                 ans++;
173         printf("%d
",ans);
174     }
175     return 0;
176 }
177 /*
178 5 4
179 1 4
180 1 5
181 2 4
182 2 5
183 
184 6 8
185 1 4 1 5 1 6
186 2 4 2 5 2 6
187 3 4 3 5
188 */
View Code

最近做了几道connectivity的题目,总结一下。

关于割顶、桥、双连通、边双连通,以及强连通处理方式有其相似性,关键都与时间戳有关。

其中,割顶->双连通 是low[v]>=dfn[u],桥->边双连通 是low[v]>dfn[u],只是一个等号的差别,其他处理基本相同;而强连通总是伴随着缩点 low[u]==dfn[u](这个一般是做边标记edge[].vis,这样即使是无向图也可以以edge[i^1].vis标记掉,而不影响重边的情况)。事实上,如果不考虑具体的桥,对边-双连通分量的划分就是在做无向图上的缩点操作。

这三个判定条件的位置也有不同。缩点是在遍历完u的邻接表之后,用每个low[v]的值更新low[u],并且u本身不会连到祖先去(这一点很重要),则是一个环,可以缩掉;在无向图中,判断双连通分量,也就是割顶(边-双连通分量&桥 一样),是每遍历一个孩子v,就要判断:low[v]>=dfn[u],只要点u的孩子所能到达的最大值不超过u,那么u就是割顶(删除u后,该子树独立),当然,u的每一个孩子v都可以是被 割顶u 分离,注意u本身是可以与它的祖先连接的!!

原文地址:https://www.cnblogs.com/zstu-abc/p/3234037.html