連結: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;
}