【Baltic2008】黑手党(Mafia)(BSOI2891)

Description

   匪徒准备从一个车站转移毒品到另一个车站,警方准备进行布控. 对于每个车站进行布控都需要一定的代价,现在警方希望使用最小的代价控制一些车站,使得去掉这些车站后,匪徒无法从原定的初始点到达目标点
    
  图中方框左上角的小数字是顶点号,中间大数字是在该点的费用。本题的最小费用是拦信1,4点,总费用是5 

Input

  第1行:2个空格分开的整数N(2≤ N ≤200)和M(1≤ M ≤20000), N表示顶点数,M表示边数。顶点编号从1~N 
  第2行:2个空格分开的整数A和B(1≤A,B≤N, A≠B),分别表示起点和终点 
  接下来N行每行描述在一个顶点的守卫费用,费用是一个不超过10000000的正整数 
  接下来M行描述图结构。每行2个整数 X 和 Y,表示在顶点X和Y之间有一条边

Output

  第1行:一列空格分开的整数,表示花最小的费用拦截物体必须守卫的顶点序列。顶点从小到大输出。 
  如果有多组解,输出字典序最小的一组解。

Sample Input

5 6
5 3
4
10 
1 5 
1 2 
2 4 
4 5 
2 3 
3 4

Sample Output

1 4

Solution

        给定一张无向图,其中节点有限制,求出最小割点集,按照字典序输出割点。                                                                                   
        网络流的基础是边限制,所以我们将一个点i拆为i‘和i’‘,并且连接i’至i‘’,容量为点限制。对于原图中存在的边(i,j),我们将i‘’连向j‘,容量为inf。我们很容易就可以通过从源点到汇点的最大流求出最小割,也就是要取出的点的最小代价。                 但是题目的要求是按字典序输出点的编号,那么我们就不能用dfs的方式来求出割点集。所以我们将原图拷贝后,依次枚举断边e。如果断边后的最大流,设为G’,和原来最大流G的差值恰好为该边的容量,那么e必为原图中的最小割。

CODE

蒟蒻的代码。                                                                                                                                                                                                   
注意代码中的cnt是初值为1,所以某点i对应的边的编号为i*2和i*2+1(WA了很多次)
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
int N,M,n;
int st,ed;
int a[205];
struct Pipe{int next,to,flow,f;}pipe[500005];//通过f标记表示该边是否存在 
Pipe tg[500005],ttg[500005];
int h[2005],cnt=1;
inline void add(int x,int y,int z){
	pipe[++cnt].to=y;pipe[cnt].next=h[x];h[x]=cnt;pipe[cnt].flow=z;pipe[cnt].f=1;
	pipe[++cnt].to=x;pipe[cnt].next=h[y];h[y]=cnt;pipe[cnt].flow=0;pipe[cnt].f=1;//双向加边,并且打上标记
        return;
}
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
int Gap[5005],dis[5005];
int ans=0,sum;
inline int SAP(int v,int maxflow){
	if(v==ed+N)return maxflow;
	int temp=maxflow,i,j,p;
	for(i=h[v];i;i=pipe[i].next){
		if(pipe[i].f==0)continue;//已删除的边就不再考虑 
		j=pipe[i].to;
		if(pipe[i].flow&&dis[v]==dis[j]+1){
			p=SAP(j,min(pipe[i].flow,temp));
			temp-=p;pipe[i].flow-=p;pipe[i^1].flow+=p;
			if(dis[st]==n||temp==0)return maxflow-temp;
		}
	}
	if(--Gap[dis[v]]==0)dis[st]=n;
	else Gap[++dis[v]]++;
	return maxflow-temp;
}
inline void GET(){
	memset(Gap,0,sizeof(Gap));
	memset(dis,0,sizeof(dis));
	Gap[0]=n;sum=0;
	while(dis[st]<n)sum+=SAP(st,inf);//得出当前网络中的最大流 
	return ;
}
void Solve(){
        int i,j,k;
	ans=sum;
	for(i=1;i<=N;i++){//只考虑删除点限度的边,而非原有的容量为inf的边 
		if(pipe[i*2].f){//该边存在 
			for(j=1;j<=cnt;j++)tg[j]=pipe[j];//拷贝 
			for(j=1;j<=cnt;j++)pipe[j]=ttg[j];//还原 
			pipe[i*2].f=0;pipe[i*2+1].f=0;
			GET();
			if(ans-sum==a[i]){//当且仅当差值为该边容量时,该边一定属于最小割集 
				cout<<i<<" ";
				ttg[i*2].f=ttg[i*2+1].f=0;//删除该边 
				ans=sum;
			}
			else for(j=1;j<=cnt;j++)pipe[j]=tg[j];//还原 
		}
	}
	return ;
}
int main(){
	N=read();M=read();st=read();ed=read();
	int i,x,y;
	for(i=1;i<=N;i++)a[i]=read();
	for(i=1;i<=N;i++)add(i,i+N,a[i]);//连上限制条件 
	for(i=1;i<=M;i++){
		x=read();y=read();
		add(x+N,y,inf);add(y+N,x,inf);//无向图 
	}
	n=N*2;//拆点后的总点数 
	for(i=1;i<=cnt;i++)ttg[i]=pipe[i];
	GET();
	Solve();
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值