链接:https://cn.vjudge.net/problem/HDU-4289
题意:多组样例。每组样例第一行给出n和m(n个城市,m条路相连)。接下来一行给出s(源点)和t(汇点)。接下来一行n个数,代表每个城市的花费。接下来m行描述图,u、v代表两个城市有路。一群不法分子会打算从s出发到目的地t。求用最小的花费,选出一个点集(即若干个城市),使得所有不法分子不能到达t。假如选择了点i(即城市i),那么经过城市i的所有不法分子都会被拦住。
思路:这打眼一看,就知道是一个网络流的题。问题是怎么用网络流解决它。因为求最小值,不难想到最小割。那么,难题就是怎么建图,使得求出的最小割就是最小花费。最小割就是把一些边割掉后,无法从源点到达汇点,这和题目要求一模一样啊。问题是,题目中的边没有边权,有点权,那么很容易想到要拆点。怎么拆呢?把每个点拆成两个点(假设为i,拆成i和i+n),点i到新添的虚点n+i的容量为点i的权值。至于给出的边(假设为u和v之间的边),把它也拆成两条边,因为是双向边,虚点n+u到点v的的容量为点u的权值,虚点n+v到点u的的容量为点v的权值。记得都加上反向弧,跑一遍Dinic求从s到n+t的最大流即可。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 410;
const int M = 1e5+10;
const int inf = 0x3f3f3f3f;
struct node
{
int to,ca,next;
}g[M];
int n,m,s,t,cnt,head[N],cur[N],deep[N],w[N];
void Init()
{
cnt=0;
memset(head,-1,sizeof(head));
return ;
}
void add(int u,int v,int w)
{
g[cnt].to=v;
g[cnt].ca=w;
g[cnt].next=head[u];
head[u]=cnt++;
return ;
}
bool bfs()
{
memset(deep,0,sizeof(deep));
queue<int> q;
int u,v;
deep[s]=1;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
if(u==t) return 1;
for(int i=head[u];i!=-1;i=g[i].next)
{
v=g[i].to;
if(g[i].ca>0&&!deep[v])
{
deep[v]=deep[u]+1;
q.push(v);
}
}
}
return deep[t]!=0;
}
ll dfs(int u,int flow)
{
if(u==t||!flow) return flow;
ll ans=0,nowflow;
int v;
for(int& i=cur[u];i!=-1;i=g[i].next)
{
v=g[i].to;
if(g[i].ca>0&&deep[v]==deep[u]+1)
{
nowflow=dfs(v,min(flow,g[i].ca));
if(nowflow)
{
flow-=nowflow;
ans+=nowflow;
g[i].ca-=nowflow;
g[i^1].ca+=nowflow;
if(!flow) break;
}
}
}
if(!ans) deep[u]=0;
return ans;
}
ll Dinic()
{
ll maxflow=0;
ll flow;
while(bfs())
{
memcpy(cur,head,sizeof(head));
while(flow=dfs(s,inf))
maxflow+=flow;
}
return maxflow;
}
int main(void)
{
int u,v;
while(~scanf("%d%d",&n,&m))
{
Init();
scanf("%d%d",&s,&t);
t=n+t;
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
add(i,n+i,w[i]);
add(n+i,i,0);
}
while(m--)
{
scanf("%d%d",&u,&v);
add(n+u,v,w[u]);
add(v,n+u,0);
add(n+v,u,w[v]);
add(u,n+v,0);
}
printf("%lld\n",Dinic());
}
return 0;
}