20160928的考试】图论(网络流,网络流,图论乱搞)

20 篇文章 0 订阅
5 篇文章 0 订阅

T1 二分图匹配 ,手贱匈牙利dfs写错了

T2 网络流   惨啊…………dinic手癌写错了,在往后增广的时候……应该用当前剩余流量……我敲成了在这个节点的所有流量……瞬间爆炸

//====================================================================================================================

T3 题意:定义两个有向图相似,当且仅当这两个图内任意两点间连通性相同(对于任意i,j∈V,如果原图i可以到达j,相似的图中i也可以到达j)。 给定一个有向图,求与它相似的图的最小边数和最大边数(没有重边和自环)(点数<=1000)


考虑若点集S(|S|>1)强连通,则边数最少时S为一个环(边数=|S|),边数最多时S为一个团(边数=|S|*(|S|-1))

缩了点后重新标号,新的图为一个DAG。显然求最大边数时可以直接把当前点能到达的所有点都连上边,随便搞搞就好了;而最小边数需要再瞎瘠薄搞一搞


对于求最小边数,原题简化为对于一个DAG,删掉尽量多的边使原图连通性不改变。

若有形如下图的结构,显然边(1,3)可以删去,

                  ①---→②-----→③

                   └--------------↗

观察得,若可以删去边(i,j),当且仅当 【对于任意点 j 可以抵达的点 ,点 i 都可以在不经过边(i,j)的情况下到达】时。

记录可达性的话……直接用bitset压一下就好

目测得,应该尽量走最长路,所以把点按照【和 i 的距离】升序枚举一遍即可(可以预先拓扑排序一下)……正确性……感性理解【捂脸.gif】


三次才A,代码准确率有待提高

一周目30’:枚举时now写成了 i

二周目60‘:建新图直接用的邻接矩阵,虽然保证了原图没有重边,但缩点后的图很容易出现重边,于是如果用0..1的邻接矩阵来记录……入度出度可能会出事……

三周目AC,代码↓

#include<bits/stdc++.h>
#define MAXN 1005
#define MAXM 100005
using namespace std;	int n,m;
struct t1{
	int to,nxt;
}edge[MAXM];	int cnt_edge=0;
int fst[MAXN];
void addedge(int x,int y){
	edge[++cnt_edge].to=y;
	edge[cnt_edge].nxt=fst[x];
	fst[x]=cnt_edge;
}

int col[MAXN],cnt_col;
int siz[MAXN];
int stk[MAXN],top=0;
int vis[MAXN],in_stk[MAXN];
int low[MAXN],dfn[MAXN],cnt_dfs=0;
void tarjan(int now){
	vis[now]=in_stk[now]=1;
	dfn[now]=low[now]=++cnt_dfs;
	stk[++top]=now;

	for(int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
		int aim=edge[tmp].to;
		if(!dfn[aim])	tarjan(aim),low[now]=min(low[now],low[aim]);
		else{
			if(in_stk[aim])	low[now]=min(low[now],dfn[aim]);
		}
	}
	
	if(dfn[now]==low[now]){
		++cnt_col;
		for(;stk[top]^now;--top)
			col[stk[top]]=cnt_col,in_stk[stk[top]]=0,++siz[cnt_col];
		col[now]=cnt_col,in_stk[now]=0,--top,++siz[cnt_col];
	}
}

int re_grp[MAXN][MAXN];
int ind[MAXN],oud[MAXN],dis[MAXN];
int que[MAXN],head,tail;
bitset<MAXN> reach[MAXN];
int cnt[MAXN];

void re_build(){
	for(int now=1;now<=n;++now){
		for(int tmp=fst[now];tmp;tmp=edge[tmp].nxt){
			int aim=edge[tmp].to;
			if(col[aim]^col[now]&&!re_grp[col[now]][col[aim]])
				re_grp[col[now]][col[aim]]=1,++ind[col[aim]],++oud[col[now]];
		}
	}
	head=tail=0;
	for(int i=1;i<=cnt_col;++i)	if(!oud[i])	que[tail++]=i,dis[i]=1;
	while(head^tail){
		int now=que[head++];
		for(int i=1;i<=cnt_col;++i){
			if(re_grp[i][now]){
				--oud[i];
				if(oud[i]==0)	que[tail++]=i,dis[i]=dis[now]+1;
			}
		}
	}
}

void work1(){
	int ans=0;
	for(int i=0;i<cnt_col;++i){
		int now=que[i];
		reach[now][now]=1;
		for(int j=i-1;~j;--j){
			int aim=que[j];
			if(re_grp[now][aim]){
/*				
				for(int k=1;k<=cnt_col;++k)	printf(reach[now][k]? "1":"0");
				puts("");
				for(int k=1;k<=cnt_col;++k)	printf(reach[aim][k]? "1":"0");
				puts("\n");
*/				
				
				if(reach[now]==(reach[now]|reach[aim]))	continue;
				else	++ans,reach[now]|=reach[aim];
			}
		}
	}
	for(int i=1;i<=cnt_col;++i)
		if(siz[i]>1)	ans+=siz[i];
	printf("%d ",ans);
}

void work2(){
	int ans=0;
	for(int i=0;i<cnt_col;++i){
		int now=que[i];
		ans+=siz[now]*(siz[now]-1);
		for(int j=0;j<i;++j){
			int aim=que[j];
			if(reach[now][aim])
				ans+=siz[now]*siz[aim];
		}
	}
	printf("%d",ans);
}

int read_x,read_y;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&read_x,&read_y);
		addedge(read_x,read_y);
	}
	for(int i=1;i<=n;++i)
		if(!dfn[i])	tarjan(i);
/*
	puts("");
	for(int i=1;i<=n;++i)
		printf("%d\n",col[i]),printf("siz = %d\n",siz[col[i]]);
//*/
	re_build();

	work1();
	work2();	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值