题意:给出一个无向图,然后有个出发城市s,结束城市 t ,然后每个点有流量限制,问你最少用多少的人能够使得 s 到 t 没有流量。
分析:题意是抽象出来的,但是很明显看出来是求最小割。难点有2
1:无向图,所以要建双向边
2:点有流量限制,所以要拆点,拆成两个点,然后这两点的容量为点的限制,图中点的连接设置流量为inf,保证割不掉,只能从点之间割。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 500;
const int inf = 0x3f3f3f3f;
int n,m;
struct Node
{
int from,to,cap,flow;
};
vector<int> v[N];
vector<Node> e;
int vis[N]; //构建层次图
int cur[N];
void add_Node(int from,int to,int cap)
{
e.push_back((Node)
{
from,to,cap,0
});
e.push_back((Node)
{
to,from,0,0
});
int tmp=e.size();
v[from].push_back(tmp-2);
v[to].push_back(tmp-1);
}
bool bfs(int s,int t)
{
Del(vis,-1);
queue<int> q;
q.push(s);
vis[s] = 0;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0; i<v[x].size(); i++)
{
Node tmp = e[v[x][i]];
if(vis[tmp.to]<0 && tmp.cap>tmp.flow) //第二个条件保证
{
vis[tmp.to]=vis[x]+1;
q.push(tmp.to);
}
}
}
if(vis[t]>0)
return true;
return false;
}
int dfs(int o,int f,int t)
{
if(o==t || f==0) //优化
return f;
int a = 0,ans=0;
for(int &i=cur[o]; i<v[o].size(); i++) //注意前面 ’&‘,很重要的优化
{
Node &tmp = e[v[o][i]];
if(vis[tmp.to]==(vis[o]+1) && (a = dfs(tmp.to,min(f,tmp.cap-tmp.flow),t))>0)
{
tmp.flow+=a;
e[v[o][i]^1].flow-=a; //存图方式
ans+=a;
f-=a;
if(f==0) //注意优化
break;
}
}
return ans; //优化
}
int dinci(int s,int t)
{
int ans=0;
while(bfs(s,t))
{
Del(cur,0);
int tm=dfs(s,inf,t);
ans+=tm;
}
return ans;
}
int num[220];
int main()
{
int T;
scanf("%d",&T);
for(int cas=1;cas<=T;cas++)
{
int s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
add_Node(i<<1,i<<1|1,num[i]);
}
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add_Node(x<<1|1,y<<1,inf);
add_Node(y<<1|1,x<<1,inf); //无向图
}
printf("%d\n",dinci(s<<1|1,t<<1));
for(int i=0;i<=2*n+2;i++)
v[i].clear();
e.clear();
}
return 0;
}