题目大意就是要从歹徒要从s点运送货物到t点,警察在一些城市拦截,在每一个城市拦截都有一定的花费,问最小花费是多少可以拦截住歹徒。
呐,在某些点设置障碍,使得整张图不能在联通,我们知道一个类似的问题:割断某些点使得图不能再联通——最小割问题,那么把这些点拆了,点权变为边权,不就是最小割问题了么,我们知道最小割等于最大流,所以这就是一个拆点+网络流的问题。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=2100,maxm=50005;
const int inf=2147483647;
int n,m;
int S,T;
int x,y;
struct zhw{
int to,last,val;
}tu[maxm<<2];
int tot,head[maxn];
void add(int x,int y,int v)
{
tot++,tu[tot].last=head[x],head[x]=tot,tu[tot].to=y,tu[tot].val=v;
tot++,tu[tot].last=head[y],head[y]=tot,tu[tot].to=x,tu[tot].val=0;
}
int ans;
bool vis[maxn];
int dis[maxn];
queue<int>q;
bool bfs()
{
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
q.push(S);vis[S]=1;dis[S]=1;
while(!q.empty())
{
int k=q.front();q.pop();
for(int i=head[k];i;i=tu[i].last)
{
if(!vis[tu[i].to]&&tu[i].val)
{
vis[tu[i].to]=1;
dis[tu[i].to]=dis[k]+1;
q.push(tu[i].to);
}
}
}
return vis[T];
}
int m_flow(int now,int lim)
{
if(!lim||now==T)return lim;
int flow=0,f;
for(int i=head[now];i;i=tu[i].last)
{
if(dis[tu[i].to]==dis[now]+1&&tu[i].val>0)
{
f=m_flow(tu[i].to,min(lim,tu[i].val));
if(!f)continue;
flow+=f,lim-=f;
tu[i].val-=f,tu[i^1].val+=f;
if(!lim)break;
}
}
if(!flow)dis[now]=-1;
return flow;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
scanf("%d%d",&x,&y);
S=x,T=y;
tot=1,memset(head,0,sizeof(head));
add(S,x,inf),add(y+n,T,inf);
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",&x,&y);
add(x+n,y,inf);//如上,既然是自己约束自己,必然是左边进右边出,路是双向边,没有约束,所以流量是正无穷
add(y+n,x,inf);
}
ans=0;
T=T+n;//这里一定要注意,最后肯定是要跑到右边的T才可以呀,我一开始就是这里错了
while(bfs())
{
ans+=m_flow(S,inf);
}
printf("%d\n",ans);
}
return 0;
}
来源:https://www.cnblogs.com/yuelian/p/12301079.html