图的联通性问题
图的联通性问题
╮(╯▽╰)╭
一 无向图的联通性问题
求割点 桥:
void dfs1(int u,int f) // f 存的是 i边 也可以存父亲 { low[u]=dfn[u]=++tim; for(ri i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(dfn[v]==0) { dfs1(v,i); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]) // >= 求割点 { ans=min(bian[i].val,ans); flag[u]=1; } if(low[v]>=dfn[u]) // >求桥 { ans=min(bian[i].val,ans); flag[i]=glag[i^1]=1; // cent初始化为 1 } } else if((i^1)!=f) low[u]=min(low[u],dfn[v]); // 可以防止重边问题; if(v!=f) low[u]=min(low[u],dfn[v]); // 存的是父亲 不能防止重边 } }
点和边的双联通分量的区别: 无穷大1和2在不属于点的双联通分量,属于边双 联通分量
求点的双联通分量:
void dfs(int u,int fa){ dfn[u]=low[u]=++tim; for(int i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(!dfn[tv]){ child++; stk[top++]=i; dfs(v,u); low[u]=min(low[u],low[tv]); if(low[tv]>=dfn[u]){ flag[u]=true; // leve++; bcc[leve].clear(); while(1){ int ii=stk[--top]; if(color[bian[ii].to]!=leve){bcc[leve].push_back(bian[ii].to);bccno[bian[ii].to]=leve;} if(color[bian[ii^1].to]!=leve){bcc[leve].push_back(bian[ii^1].to);bccno[bian[ii^1].to]=leve;} if(bian[ii].to==v&&bian[ii^1]==u)break; } } } else if(tv!=fa){ stk[top++]=i; // 这个别忘记了 low[u]=min(low[u],dfn[tv]); } } if(fa==0&&child==1) flag[u]=0; // 割点的特判 }
求边的双联通分量:
void dfs1(int u,int f) //求桥 { low[u]=dfn[u]=++tim; for(ri i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(dfn[v]==0) { dfs1(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) { flag[i]=flag[i^1]=1; } } else if(v!=f) low[u]=min(low[u],dfn[v]); } } int leve2=1; void dfs2(int u,int leve) // 标记 { color[u]=leve; vis[u]=1; for(ri i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(vis[v]==0) { if(flag[i]==1) { leve2++; dfs2(v,leve2); } else dfs2(v,leve); } } }
有向图的强联通
void dfs1(int u) { dfn[u]=low[u]=++tim; stk[top++]=u; // 不一样 想一想 无穷符号 for(ri i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(dfn[v]==0) { dfs1(v); low[u]=min(low[u],low[v]); } else if(color[v]==0) low[u]=min(low[u],dfn[v]); // 不一样的地方 防止到别的环上 不可能到父亲 } if(dfn[u]==low[u]) // 不一样 { leve++; while(top>0) { int v=stk[--top]; color[v]=leve; if(v==u) break; } } }
无向图求加多少条边变成整个双联通
缩点后 根节点的数量+1除以2;
for(ri i=1;i<=n;i++) for(ri j=head[i];j;j=bian[j].net) { int v=bian[j].to; if(color[v]!=color[i]) num[color[v]]++; } for(ri i=1;i<=leve2;i++) { if(num[i]==1) ans++; } printf("%d\n",(ans+1)/2);
有向图加多少条边和变成强联通:
缩点后的入读出度为0 的总的点数量最大的;
for(ri i=1;i<=n;i++) for(ri j=head[i];j;j=bian[j].net) { int v=bian[j].to; if(color[i]!=color[v]) { in[color[i]]++; out[color[v]]++; } } for(ri i=1;i<=leve;i++) { if(in[i]==0) IN++; if(out[i]==0) OUT++; } if(leve==1) printf("0\n"); // 已经是强联通 else printf("%d\n",max(IN,OUT)); }
题目:
成为一名骑士是一项非常诱人的职业:寻找圣杯,拯救处于困境的少女以及与其他骑士共饮是一件有趣的事情。因此,近年来亚瑟王王国的骑士人数空前增加,这并不奇怪。现在有这么多的骑士,很少有每个圆桌骑士能够同时来卡梅洛特并坐在圆桌旁的。通常只有一小部分骑士,而其余的骑士则忙着在全国各地做英勇事迹。
在讨论过程中,骑士特别容易喝酒,容易引起过度兴奋。在发生一些不幸的事故之后,亚瑟王要求著名的巫师梅林(Merlin)确保以后骑士之间不会发生战斗。在仔细研究了问题之后,Merlin意识到只有按照以下两个规则就座骑士,才能避免打架:
在讨论过程中,骑士特别容易喝酒,容易引起过度兴奋。在发生一些不幸的事故之后,亚瑟王要求著名的巫师梅林(Merlin)确保以后骑士之间不会发生战斗。在仔细研究了问题之后,Merlin意识到只有按照以下两个规则就座骑士,才能避免打架:
- 骑士的座位应确保彼此仇恨的两个骑士不应该是餐桌上的邻居。(Merlin列出了谁讨厌谁的名单。)骑士们坐在圆桌旁,因此每个骑士都有两个邻居。
- 桌子周围应该有奇数个骑士。这样可以确保如果骑士们无法达成共识,那么他们可以通过投票解决问题。(如果骑士的数量是偶数,那么可能会发生“是”和“否”具有相同的投票数,并且这种说法会持续下去。)
输入项
输入包含几个测试用例块。每种情况都从包含两个整数1≤n≤1000和1≤m≤1000000的行开始。数字n是骑士数。接下来的m行描述哪个骑士讨厌哪个骑士。这m行中的每行包含两个整数k1和k2,这意味着骑士数k1和骑士数k2彼此讨厌(数字k1和k2在1到n之间)。
输入由n = m = 0的块终止。
输入由n = m = 0的块终止。
输出量
对于每个测试用例,您必须在单独的一行上输出一个整数:必须驱逐的骑士数。
样本输入
5 5 1 4 1 5 2 5 3 4 4 5 0 0
样本输出
2
题解 求点的双联通分量,和二分图的判断(二分图一点是个偶图^_^)
注意 二分图的判断时 不能 return 二分图判断(v,k) 应为儿子还没有访问完,
代码

#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<stack> using namespace std; const int M =400010; #define ri register int struct setbian{ int net,to; }bian[M]; int head[M],cent,arr[1005][1005],low[M],dfn[M],tim,stk[M],top,color[M],f[M],trmp[M],leve; vector <int> bcc[M]; void add(int a,int b) { bian[++cent].net=head[a],head[a]=cent; bian[cent].to=b; } void dfs1(int r,int f) { low[r]=dfn[r]=++tim; for(ri i=head[r];i;i=bian[i].net) { int v=bian[i].to; if(dfn[v]==0) { stk[top++]=v; dfs1(v,r); low[r]=min(low[r],low[v]); if(low[v]>=dfn[r]) { leve++; bcc[leve].clear(); while(top>0) { int v2=stk[--top]; bcc[leve].push_back(v2); if(v==v2) break; } bcc[leve].push_back(r); } } else if(v!=f) low[r]=min(low[r],dfn[v]); } } int dfs2(int r,int num) { for(ri i=head[r];i;i=bian[i].net) { int v=bian[i].to; if(f[v]!=num) continue; if(color[v]==color[r]) return 0; if(color[v]==0) { color[v]=3-color[r]; if(!dfs2(v,num)) return 0; // return dfs2(v,num) 这个是错的 } } return 1; } int n,m; int main(){ while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; memset(arr,0,sizeof arr); memset(head,0,sizeof head); memset(low,0,sizeof low); memset(dfn,0,sizeof dfn); memset(color,0,sizeof color); memset(stk,0,sizeof stk); memset(f,0,sizeof f); memset(trmp,0,sizeof trmp); top=cent=leve=tim=0; int a,b; for(ri i=1;i<=m;i++) // scanf("%d%d",&a,&b),arr[a][b]=arr[b][a]=1; for(ri i=1;i<=n;i++) for(ri j=1;j<=n;j++) { if(i!=j&&arr[i][j]==0) // add(i,j); } for(ri i=1;i<=n;i++) // {//cout<<"yes"<<endl; if(dfn[i]==0) dfs1(i,0); } for(ri i=1;i<=leve;i++) // { memset(color,0,sizeof color); for (ri j=0;j<bcc[i].size();j++) { int v=bcc[i][j]; f[v]=i; } color[bcc[i][0]]=1; if(!dfs2(bcc[i][0],i)) // { for(ri j=0;j<bcc[i].size();j++) { trmp[bcc[i][j]]=1; } } } int ans=n; // for(ri i=1;i<=n;i++) { if(trmp[i]==1) ans--; } printf("%d\n",ans); } return 0; }
例题 :
在赤壁战役中,曹操被诸葛亮和周瑜击败。但是他不会放弃。曹操的部队仍不擅长水战,因此他想出了另一个主意。他在长江上建了许多岛屿,在这些岛屿的基础上,曹操的军队可以轻松地攻击周瑜的部队。曹操还修建了连接各岛的桥梁。如果所有岛屿都通过桥梁连接起来,那么曹操的军队可以很方便地在这些岛屿之间部署。周瑜不能忍受,所以他想摧毁一些草桥,以使一个或多个岛屿与其他岛屿分开。但是周瑜只有诸葛亮留下的一颗炸弹,所以他只能摧毁一座桥。周瑜必须派人携带炸弹摧毁这座桥。桥梁上可能会有警卫。轰炸队的士兵人数不得少于桥梁的守卫人数,否则任务将失败。请至少弄清楚周瑜必须派多少士兵来完成离岛任务。
输入项测试案例不超过12个。
在每个测试案例中:
第一行包含两个整数N和M,这意味着有N个岛和M个桥。所有岛的编号从1到N。(2 <= N <= 1000,0 <M <= N 2)
接下来的M行描述了M个桥。每行包含三个整数U,V和W,这意味着有一个连接孤岛U和孤岛V的桥,并且该桥上有W个防护装置。(U≠V并且0 <= W <= 10,000)
输入以N = 0和M = 0结尾。输出量对于每个测试用例,打印出周瑜为完成任务而必须发送的最小士兵人数。如果周瑜无法成功,请打印-1。样本输入
3 3
1 2 7
2 3 4
3 1 4
3 2
1 2 7
2 3 4
0 0
题解 : 求桥,用 i 作为fa 就不用判读重边的问题。直接加进去 ,台秀了╮(╯▽╰)╭


#include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAXN =1010; #define ri register int struct setbian{ int net,to,val; }bian[MAXN*MAXN+10]; int head[MAXN],cent=1,tim,low[MAXN],dfn[MAXN],color[MAXN*MAXN+20],leve,num,ans=0x7fffffff; void add(int a,int b,int c) { bian[++cent].net=head[a],head[a]=cent; bian[cent].to=b,bian[cent].val=c; } void dfs1(int u,int f) // f 是边 { low[u]=dfn[u]=++tim;num++; for(ri i=head[u];i;i=bian[i].net) { int v=bian[i].to; if(dfn[v]==0) { dfs1(v,i); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) { ans=min(bian[i].val,ans); } } else if((i^1)!=f) low[u]=min(low[u],dfn[v]); } } int n,m; int main(){ while(~scanf("%d%d",&n,&m)) { memset(head,0,sizeof head); memset(low,0,sizeof low); memset(dfn,0,sizeof dfn); memset(bian,0,sizeof bian); cent=1;ans=0x7fffffff; tim=num=0; if(n==0&&m==0) break; for(ri i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs1(1,-1); if(num!=n) { printf("0\n");continue; } if(ans==0x7fffffff){ printf("-1\n");continue;} if(ans==0) printf("1\n"); else printf("%d\n",ans); } return 0; }