[BZOJ1322]Destroying The Graph

题目大意:
有一张有向图,对于每个点,有两种操作:
1. 删掉它的所有入边
2. 删掉它的所有出边
对每个点的每个操作均有不同的价值。
求使得图上没有边的最小价值。
解题思路:
考虑把点拆成入点和出点,然后就是二分图最小点权覆盖集。
也可以考虑最小割。
从S到每个点的入点连容量为该点执行操作2的价值,每个点的出点到T连容量为该点执行操作1的价值。对于图上的每条边连容量inf的边。
然后答案就是最小割(割一条S出发的边,相当于执行了2操作,网络流不可能从该点再流向其他节点,则相当于删掉出边。操作1同理)。

C++ Code:

#include<bits/stdc++.h>
using namespace std;
const int S=0,T=10005,inf=0x3fffffff;
struct edge{
	int to,nxt,cap;
}e[200005];
int head[10050],cnt=1,n,m,level[10050],iter[10050];
inline void addedge(int from,int to,int flow){
	e[++cnt]=(edge){to,head[from],flow};
	head[from]=cnt;
	e[++cnt]=(edge){from,head[to],0};
	head[to]=cnt;
}
queue<int>q;
void bfs(){
	level[S]=1;
	for(q.push(S);!q.empty();){
		int u=q.front();
		q.pop();
		for(int i=head[u];~i;i=e[i].nxt)
		if(e[i].cap&&!~level[e[i].to]){
			level[e[i].to]=level[u]+1;
			q.push(e[i].to);
		}
	}
}
inline int min(int a,int b){return a<b?a:b;}
int dfs(int u,int f){
	if(!f||u==T)return f;
	for(int& i=iter[u];~i;i=e[i].nxt)
	if(e[i].cap&&level[e[i].to]>level[u]){
		int d=dfs(e[i].to,min(f,e[i].cap));
		if(d){
			e[i].cap-=d;
			e[i^1].cap+=d;
			return d;
		}else level[e[i].to]=-1;
	}
	return 0;
}
int dinic(){
	for(int flow=0,f;;){
		memset(level,-1,sizeof iter);
		if(bfs(),!~level[T])return flow;
		memcpy(iter,head,sizeof iter);
		while(f=dfs(S,inf))flow+=f;
	}
}
int main(){
	memset(head,-1,sizeof head);
	ios::sync_with_stdio(false);cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		int p;
		cin>>p;
		addedge(i+n,T,p);
	}
	for(int i=1;i<=n;++i){
		int p;
		cin>>p;
		addedge(S,i,p);
	}
	while(m--){
		int x,y;
		cin>>x>>y;
		addedge(x,y+n,inf);
	}
	cout<<dinic()<<endl;
	return 0;
}

 

转载于:https://www.cnblogs.com/Mrsrz/p/9304310.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值