1. 程式人生 > >[BZOJ1322]Destroying The Graph

[BZOJ1322]Destroying The Graph

bzoj names graph spa cap esp end 操作 網絡流

題目大意:
有一張有向圖,對於每個點,有兩種操作:
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;
}

[BZOJ1322]Destroying The Graph