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

图中方框左上角的小数字是顶点号,中间大数字是在该点的费用。本题的最小费用是拦信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之间有一条边
第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
2
4
8
3
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;
}