刷题记录 kuangbin带你飞专题九:连通图

这个专题实属虐心…各种debug搞到自闭
不过进步就是在自闭中产生的…
后面被反向边逼迫使用了链式前向星建图(双修?)

1.POJ 1236 Network of Schools

任务一:求出入度为0的连通分量数量
任务二:求加多少条边能让整个图强连通
对于第二问,因为要使整个图强连通,贪心的拿每一个出度为0的点对入度为0的点加边,这样下来最小的加边数就是max(入度,出度)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=1e3+5,maxm=1e3+5;
int n,m,chu[maxn];
int dfn[maxn],low[maxn],vis[maxn],z[maxn],color[maxn],cnt[maxn],ru[maxn],t,k,tot;
vector<int>e[maxm];
void tarjan(int u,int fa){
	dfn[u]=low[u]=++tot;
	z[++k]=u;//入栈 
	vis[u]=1;
	for(int i=0;i<e[u].size();i++){
		if(!dfn[e[u][i]]){
			tarjan(e[u][i],fa);
			low[u]=min(low[u],low[e[u][i]]);
		}
		else if(vis[e[u][i]]){
			low[u]=min(low[u],dfn[e[u][i]]);
		}
	}
	if(low[u]==dfn[u]){
		t++;//连通分量的标号 
		do{
			color[z[k]]=t;    //属于这个连通分量 
			cnt[t]++;         //记录这个环中有多少个点
			vis[z[k]]=0; 
			k--;              //出栈 
		}while(u!=z[k+1]);
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		while(1){
		    int v;
	    	scanf("%d",&v);
		    if(v==0)break;
	    	e[i].push_back(v);	
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i])tarjan(i,i);
	}
	int ans=0,maxx=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<e[i].size();j++){
			if(color[i]!=color[e[i][j]]){
				ru[color[e[i][j]]]++;
				chu[color[i]]++;
			}
		}
	}
	int x=0,y=0;
	for(int i=1;i<=t;i++){
		if(ru[i]==0)x++;
		if(chu[i]==0)y++;
	}
	if(t==1)cout<<x<<endl<<"0";
	else cout<<x<<endl<<max(x,y);
	return 0;
}

2.UVA 315 Network

求割点
uva读入恶心人

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=2e4+5,maxm=2e4+5;
int n,m;
bool cut[maxn];
int dfn[maxn],low[maxn],vis[maxn],z[maxn],color[maxn],cnt[maxn],du[maxn],t,k,tot;
vector<int>e[maxm];
void tarjan(int u,int root){//root代表此树的根 
	int child=0;
	dfn[u]=low[u]=++tot;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(!dfn[v]){
			tarjan(v,root);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]&&u!=root)cut[u]=true;
			if(u==root)child++;
		}
		low[u]=min(low[u],dfn[v]);
	}
	if(u==root&&child>=2){
		cut[root]=true;
	}
}
void init(int n){
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(cut,false,sizeof(cut));
	for(int i=0;i<=n;i++){
		e[i].clear();
	}
}
int main(){
	int u,v;
	char c;
	while(cin>>n){
		if(n==0)break;
		init(n);
		while (scanf("%d", &u), u) {
			while (scanf("%c", &c), c != '\n') {
				scanf("%d", &v);
				e[u].push_back(v);
				e[v].push_back(u);
			}
		}
		for(int i=1;i<=n;i++){
	    	if(!dfn[i])tarjan(i,i);
    	}
    	int ans=0;
    	for(int i=1;i<=n;i++){
	    	if(cut[i])ans++;
    	}
    	cout<<ans<<endl;
	}
	return 0;
}

3.UVA 796 Critical Links

求桥
uva读入再次恶心人
对于求桥有不同的写法,有时候会出现玄学错误…

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=2e5+5,maxm=5e5+5;
int n,m;
int dfn[maxn],low[maxn],t,k,tot;
vector<int>e[maxm];
struct edge{
	int u,v;
}qiao[maxm];
bool cmp(edge a,edge b){
	if(a.u==b.u)return a.v<b.v;
	return a.u<b.u;
}
void tarjan(int u,int root){//root代表此树的根 
	dfn[u]=low[u]=++tot;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){
				t++;
				qiao[t].u=min(u,v);
				qiao[t].v=max(u,v);
			}
		}
		else if(v!=root)low[u]=min(low[u],dfn[v]);
	}
	return;
}
void init(int n){
	t=0;
	tot=0;
	k=0;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	for(int i=0;i<=n;i++){
		e[i].clear();
	}
}
int main(){
	while(cin>>n){
	init(n);
	int u,v,num;
	char c1,c2;
	for(int i=1;i<=n;i++){
		cin>>u>>c1>>num>>c2;
		u++;
		while(num--){
			cin>>v;
			v++;
			e[u].push_back(v);
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i])tarjan(i,0);
	}
	sort(qiao+1,qiao+1+t,cmp);
	printf("%d critical links\n",t);
	for(int i=1;i<=t;i++){
    	printf("%d - %d\n",qiao[i].u-1,qiao[i].v-1);
	}
	cout<<endl;
}
	return 0;
}

4.POJ 3694 Network

求加边的过程中,桥的数量
正解应该是每次加边后求出lca,加入这条边之后这个环上面的边就都不是桥了
借鉴了大神的题解的方法,在求桥的过程中求出每个点的深度,然后用check函数将两个点逐渐跳到lca,并将路径上的桥消去

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=2e5+5,maxm=4e5+5,inf=0x3f3f3f3f;
int dfn[maxn],low[maxn],fa[maxn],dep[maxn],qiao[maxn],n,m,t,tot;
vector<int>e[maxm*2];
void tarjan(int u,int root){
	dfn[u]=low[u]=++tot;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==root)continue;
		if(!dfn[v]){
			fa[v]=u;
			dep[v]=dep[u]+1;
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){
				t++;
				qiao[v]=1;
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
	return;
}
void check(int u,int v){
	while(dep[u]>dep[v]){
		if(qiao[u])t--,qiao[u]=0;
		u=fa[u];
	}
	while(dep[v]>dep[u]){
		if(qiao[v])t--,qiao[v]=0;
		v=fa[v];
	}
	while(u!=v){
		if(qiao[u])qiao[u]=0,t--;
		if(qiao[v])qiao[v]=0,t--;
		u=fa[u];
		v=fa[v];
	}
}
void qibao(int n){
	memset(low,0,sizeof(low));
	memset(dfn,0,sizeof(dfn));
	memset(fa,0,sizeof(fa));
	memset(dep,0,sizeof(dep));
	memset(qiao,0,sizeof(qiao));
	t=tot=0;
	for(int i=0;i<=n;i++){
		e[i].clear();
	}
}
int main(){
	int n,m,u,v,tt=1;
	while(scanf("%d%d",&n,&m)!=EOF){
		if(n==0&&m==0)break;
		qibao(max(n,m));
		for(int i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			e[u].push_back(v);
			e[v].push_back(u);
		}
		cin>>m;
		for(int i=1;i<=m;i++){
			if(!dfn[i])tarjan(i,i);
		}
		printf("Case %d:\n",tt++);
		for(int i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			check(u,v);
			cout<<t<<endl;
		}
		cout<<endl;
	}
	return 0;
}

5.POJ 3177 Redundant Paths

问加多少条边可以让图成为双连通图
先缩点
入度为1的点数量为ans
答案为(ans+1)/2
这是怎么来的呢?将入度为1的点两两相连就可以了,所以点数为奇数的话要+1

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=1e5+5,maxm=1e6+5,inf=0x3f3f3f3f;
int low[maxn],dfn[maxn],tot,k,t,z[maxn],head[maxm],len=1;
int color[maxm],ru[maxn];
struct edge{
	int v,nex;
}e[maxm];
void add_edge(int u,int v){
	e[++len].v=v;
	e[len].nex=head[u];
	head[u]=len;
}
void tarjan(int u,int root){
	dfn[u]=low[u]=++tot;
	z[++k]=u;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(!dfn[v]){
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
		}
		else if(i!=(root^1))low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		t++;//连通分量的标号 
		do{
			color[z[k]]=t;    //属于这个连通分量 
			k--;              //出栈 
		}while(u!=z[k+1]);
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add_edge(u,v);
		add_edge(v,u);
	}
	tarjan(1,0);
	int ans=0;
	for(int u=1;u<=n;u++){
		for(int j=head[u];j;j=e[j].nex){
			int v=e[j].v;
			if(color[u]!=color[v])ru[color[v]]++;
		}
	}
	for(int i=1;i<=t;i++){
		if(ru[i]==1)ans++;
	}
	cout<<(ans+1)/2;
}

6.HDU 4612 Warm up

缩点建树+树的直径
缩点后得到一棵树
因为树上每条边都是桥,所以桥的数量=联通分量数-1
问加一条边能让桥的数量最少为多少
树的直径的上的桥最多
所以用桥的数量-树的直径即为答案

wa到哭,让我晚上整个人都清醒了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn=2e5+5,maxm=2e6+5,inf=0x3f3f3f3f;
int low[maxn],dfn[maxn],tot,top,t,z[maxn],head[maxn],len,len2,d;
int color[maxn],head2[maxn],vis[maxn],ans,n,m;
struct edge{
	int v,nex;
}e[maxm],e2[maxm];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
void init(){
	memset(head,-1,sizeof(head));
	memset(vis,0,sizeof(vis));
	memset(dfn,0,sizeof(dfn));
	tot=0,t=0,len=0,len2=0,top=0,d=0;
}
inline void add_edge(int u,int v){
	e[len].v=v;
	e[len].nex=head[u];
	head[u]=len++;
}
inline void add_edge2(int u,int v){
	e2[len2].v=v;
	e2[len2].nex=head2[u];
	head2[u]=len2++;
}
void build(){
	len2=0;
	memset(head2,-1,sizeof(head2));
	for(int u=1;u<=n;u++){
		for(int j=head[u];j!=-1;j=e[j].nex){
			int v=e[j].v;
			if(color[u]!=color[v]){
				add_edge2(color[u],color[v]);
			}
		}
	}
}
void tarjan(int u,int root){
	low[u]=dfn[u]=++tot;
	vis[u]=1;
	z[top++]=u;
	int flag=1;
	for(int i=head[u];i!=-1;i=e[i].nex){
		int v=e[i].v;
		if(v==root&&flag){
			flag=0;
			continue;
		}
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		t++;
		int v;
		while(1){
			v=z[top-1];
			top--;
			color[v]=t;
			vis[v]=0;
			if(v==u)break;
		}
	}
}
int dfs(int u,int fa){
	int d1=0,d2=0;
	for(int i=head2[u];i!=-1;i=e2[i].nex){
		int v=e2[i].v;
		if(v==fa)continue;
		int d=dfs(v,u)+1;
		if(d>d1)d2=d1,d1=d;
		else if(d>d2)d2=d;
	}
	ans=max(ans,d1+d2);
	return d1;
}
int main(){
	int u,v;
	while(scanf("%d%d",&n,&m)&&(n+m)){
	init();
	for(int i=1;i<=m;i++){
		u=read(),v=read();
		if(u==v)continue;
		add_edge(u,v);
		add_edge(v,u);
	}
	tarjan(1,-1);
	build();
	ans=0;
	dfs(1,-1);
	printf("%d\n",t-ans-1);
	}
	return 0;
}

7.HDU 4635 Strongly connected

给定一个有向图,求最大可以增加多少条边使得这个仍然不是强连通
首先要知道一点:完全图的边数为n*(n-1)
然后减去图中本来就有的m条边
再减去入度或出度为0的点能跟其他点连的边or被其他点连的边,即为最终答案

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define int long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
int n,m,chu[maxn],qq,a[maxn],b[maxn];
int dfn[maxn],low[maxn],vis[maxn],z[maxn],color[maxn],cnt[maxn],ru[maxn],t,k,tot;
vector<int>e[maxm];
void tarjan(int u){
	dfn[u]=low[u]=++tot;
	z[++k]=u;//入栈 
	vis[u]=1;
	for(int i=0;i<e[u].size();i++){
		if(!dfn[e[u][i]]){
			tarjan(e[u][i]);
			low[u]=min(low[u],low[e[u][i]]);
		}
		else if(vis[e[u][i]]){
			low[u]=min(low[u],dfn[e[u][i]]);
		}
	}
	if(low[u]==dfn[u]){
		t++;//连通分量的标号 
		do{
			color[z[k]]=t;    //属于这个连通分量 
			cnt[t]++;         //记录这个环中有多少个点
			vis[z[k]]=0; 
			k--;              //出栈 
		}while(u!=z[k+1]);
	}
}
void init(int n){
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(z,0,sizeof(z));
	memset(vis,0,sizeof(vis));
	memset(color,0,sizeof(color));
	memset(cnt,0,sizeof(cnt));
	memset(ru,0,sizeof(ru));
	memset(chu,0,sizeof(chu));
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	for(int i=0;i<=n;i++){
		e[i].clear();
	}
	t=0;
	k=0;
	tot=0;
}
signed main(){
	int tt;
	cin>>tt;
	while(tt--){
	qq++;
	cin>>n>>m;
	init(max(n,m));
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		a[i]=u;
		b[i]=v;
		e[u].push_back(v);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i])tarjan(i);
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<e[i].size();j++){
			int u=color[i];
			int v=color[e[i][j]];
			if(u!=v){
				chu[u]++;
				ru[v]++;
			}
		}
	}
	int minv=n;
	for(int i=1;i<=t;i++){
		if(ru[i]==0||chu[i]==0)minv=min(minv,cnt[i]);
	}
	int ans=n*(n-1)-m-minv*(n-minv);
	if(t==1)printf("Case %lld: -1\n",qq);
	else printf("Case %lld: %lld\n",qq,ans);
	}
	return 0;
}

8.HDU 4685 Prince and Princess

夹带私货的题,二分图完美匹配+缩点,之后补(虽然会用最大流求最大匹配数)

9.HDU 4738 Caocao’s Bridges

又是一题坑的我爆炸的题
用之前的找桥的tarjan疯狂无敌爆炸wa
以后求桥都用这个 链式前向星+反向边来求
其实这个题很简单,求桥的过程中更新桥的最小权值就可以了,如果图本来就不连通就输出-1,特判最小权值为0,因为最少还是需要一个人去炸桥,所以为1

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int maxn=2e4+5,maxm=2e6+5,inf=0x3f3f3f3f;
int n,m;
int dfn[maxn],low[maxn],k,tot,ans,tcnt,head[maxn],cnt;
int qiao[maxm];
struct edge{
	int v,w,nex;
}e[maxm];
inline void add(int u,int v,int w){
	e[++cnt].nex=head[u];
	e[cnt].v=v;
	e[cnt].w=w;
	head[u]=cnt;
}
void tarjan(int u,int root){//root代表此树的根 
	dfn[u]=low[u]=++tot;
	for(int i=head[u];i!=-1;i=e[i].nex){
		int v=e[i].v;
		if(!dfn[v]){
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){
				ans=min(ans,e[i].w);
			}
		}
		else if(i!=(root^1))low[u]=min(low[u],dfn[v]);
	}
	return;
}
void init(){
	tot=0;
	k=0;
	cnt=1;
	tcnt=0;
	ans=inf;
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(head,-1,sizeof(head));
}
int main(){
	while(cin>>n>>m){
	if(n==0&&m==0)break;
	init();
	int u,v,w;
	for(int i=1;i<=m;i++){
		cin>>u>>v>>w;
		add(u,v,w);
		add(v,u,w);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i,0);
			tcnt++;
		}
	}
	if(tcnt>1)puts("0");
    else if(ans==inf)puts("-1");
	else if(ans==0)puts("1");
	else printf("%d\n",ans);
    }
	return 0;
}

刷题记录不是题解,写的十分简略,见谅qwq
自闭自闭自闭自闭自闭自闭…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值