题意:有n个城市和m条道路(双向),一伙小偷准备从S城出发到H城盗窃,为了将这伙小偷抓住,需要在这n个城市中的每一个城市安排一定数量的警察(每个城市警察的数量已经给出),但警察不希望在S城或H城遇到小偷.求解总共需要的最少警察数.
由于每个城市顶点都具有权值,所以对于每个城市拆成两个点u和所对应的u',之间连容量为w的边,S,H两点不会算在最小割中,所以将这两点拆点,拆点后容量为无穷,添加源点s和汇点t,加边(s,S,INF)和(H+n,t,INF),对于两相连的城市之间连容量为无穷大的双向边,然后求解最大流即可.
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=400 ;
const int M=300000 ;
const int inf=1<<30 ;
struct node
{
int u , v, c, next ;
}edge[M];
int head[N],gap[N],dis[N],cur[N],pre[N];
int top ;
void add(int u, int v ,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int sap(int s,int t,int n)
{
int flow=0,aug=inf,u;
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
for(int i=0; i<=n; i++) cur[i]=head[i];
gap[s]=n;
u=pre[s]=s;
while(dis[s]<n)
{
loop :
for(int &j=cur[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].c>0&&dis[u]==dis[v]+1)
{
if(edge[j].c<aug) aug=edge[j].c;
pre[v]=u;
u=v;
if(v==t)
{
while(u!=s)
{
u=pre[u];
edge[cur[u]].c-=aug;
edge[cur[u]^1].c+=aug;
}
flow+=aug;
aug=inf;
}
goto loop ;
}
}
int mindis=n;
for(int j=head[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].c>0&&dis[v]<mindis)
{
mindis=dis[v];
cur[u]=j;
}
}
if((--gap[dis[u]])==0)
break;
gap[dis[u]=mindis+1]++;
u=pre[u];
}
return flow;
}
int main()
{
int T,n,m,S,H,x,a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&S,&H);
top=0;
memset(head,-1,sizeof(head));
int s=0,t=2*n+1;;
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&x);
add(i,i+n,x);
}
for(int i = 1 ; i <= m ; i++)
{
scanf("%d%d",&a,&b);
add(a+n,b,inf); //双向边
add(b+n,a,inf);
}
add(s,S,inf); //源点到起点
add(H+n,t,inf); //终点到汇点
add(S,S+n,inf); //S,H两点容量改为inf,因为这两点说不能割;
add(H,H+n,inf);
int ans=sap(s,t,t+1);
printf("%d\n",ans);
}
return 0;
}