省选专练AHOI2009最小割

本文探讨了最大流与最小割的关系,并通过具体算法实现,解析了如何判断一条边是否属于最小割,同时讨论了两个关于最小代价路径切断方案的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

考察:最小割的真正含义

我们知道最大流在意义上与最小割完全不同

但是最大流在数值上与最小割一样

本题也就是考察了在最大流上跑最小割的含义。

如何断定一条边被割断:反向边流量为w原边流量为0

这说明水流全部从这里流走了,也就是最小割。

看问题:

问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。

请冷静思考:

这现在的参量网路是一张类分层图。

只要把割断的边无视,那么这就是几块。

而且这也就成了混合图

我们考虑强联通关系。

第一S与T一定不在一起,否则图并没有割断。

其次当一条边(W=0):U,V的U,V不在一起,那么他们一定可以被选择在一条最小割上

因为割断它图并没有联通。

对于问题二,只要U和S在一起V和T在一起那么说明他们必须被割,否则图永远联通。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1000;
const int INF=1e9+7;
inline void read(int &x){
	x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
struct Front_star{
	int u,v,w,nxt;
}e[N*2];
int cnt=1;
int first[N]={};
void addedge(int u,int v,int w){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].nxt=first[u];
	first[u]=cnt;
}
void add(int u,int v,int w){
	addedge(u,v,w);
	addedge(v,u,0);
}
int S;
int T;
int n,m;
int d[N]={};
queue<int> q;
bool BFS(){
	for(int i=0;i<=n;i++)d[i]=-1;
	d[S]=1;
	q.push(S);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=first[x];i;i=e[i].nxt){
			int v=e[i].v;
			if(e[i].w&&d[v]==-1){
				d[v]=d[x]+1;
				q.push(v);
			}
		}
	}
	return d[T]!=-1;
}
int dfs(int st,int ed,int nowdat){
	if(st==ed){
		return nowdat;
	}
	int dat=0;
	for(int i=first[st];i;i=e[i].nxt){
		int v=e[i].v;
		if(e[i].w&&d[v]==d[st]+1){
			int now=min(nowdat-dat,e[i].w);
			now=dfs(v,ed,now);
			e[i].w-=now;
			e[i^1].w+=now;
			dat+=now;
			if(nowdat==dat){
				return dat;
			}
		}
	}
	if(!dat){
		d[st]=-2;
	}
	return dat;
}
int Dinic(){
	int ret=0;
	while(BFS()){
		ret+=dfs(S,T,INF);
	}	
	return ret;
}
//
int dfn[N]={};
int low[N]={};
int color[N]={};
int vis[N]={};
int scc=0;
int tot=0;
stack<int> St;
void tarjan(int u){
	vis[u]=1;
	St.push(u);
	tot++;
	dfn[u]=tot;
	low[u]=tot;
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(e[i].w){
			if(!dfn[v]){
				tarjan(v);
				low[u]=min(low[u],low[v]);
			}
			else{
				if(vis[v]){
					low[u]=min(low[u],dfn[v]);
				}
			}
		}
	}
	if(low[u]==dfn[u]){
		int x;
		scc++;
		do{
			x=St.top();
			vis[x]=0;
			color[x]=scc;
			St.pop();
		}while(x!=u);
	}
}
int main(){
	read(n);
	read(m);
	read(S);
	read(T);
	for(int i=1;i<=m;i++){
		int u,v,w;
		read(u);
		read(v);
		read(w);
		add(u,v,w);
	}
	Dinic();
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i);
		}
	}
	for(int i=2;i<=cnt;i+=2){
		if(e[i].w)puts("0 0");
		else{
			if(color[e[i].u]!=color[e[i].v])printf("1 ");
			else printf("0 ");
			if(color[S]==color[e[i].u]&&color[T]==color[e[i].v])puts("1");
			else puts("0");
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值