紫书上网络流部分的第一道例题, 刚刚学了最大流,还没有理解二分图匹配 , 这里就只说一下我用最大流是怎么做的吧 。
我们可以假想一个源点,一个汇点,然后对于每一个设备的插头,从源点连一条线,对于每个插座,连一条线到汇点,且容量都为1 。 然后对于每一个转换器,从原插头到变换后的插头连一条边,因为转换器数量无穷大,所以容量为无穷大 。
这样我们就将原问题抽象成了最大流问题,巧妙的将出入路径的容量赋值为1,以此来让每一个设备匹配到唯一一个容器 。紫书上说这是二分图匹配,也许也暗含着其原理吧 。
最大流问题好像适合解决那种匹配问题, 因为其无序性,我们很难定义阶段,而且状态还有可能回到之前的状态,所以不能运用动态规划解决。
另外通过该题也可以看出,要想应用网络流首先应该将实际问题抽象出来,将具体意义的性质或量抽象成网络流模型中的流量或者容量。
补充一下,该题确实是二分图匹配,通过添加源点和汇点利用最大流实现的最大匹配问题,可看这里:点击打开链接
细节参见代码:
#include<bits/stdc++.h>
using namespace std;
const int INF = 100000000;
const int maxn = 505;
int T,cnt,n,m,t,k,a[maxn],p[maxn];
char s[maxn],buf[maxn];
map<string,int> pp;
struct Edge{
int from,to,cap,flow;
Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f) {}
};
vector<Edge> edges;
vector<int> g[maxn];
void init() {
for(int i=0;i<maxn;i++) g[i].clear();
edges.clear();
}
void addedge(int from,int to,int cap) { //增加边并将每个节点对应的边保存在g中
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
t = edges.size();
g[from].push_back(t-2);
g[to].push_back(t-1);
}
int maxflow(int s,int t) {
int flow = 0; //最大流初始化为0
for(;;) { //核心算法,需要注意,我们一开始加进来的边的流量都是0,通过求最小残量逐步增广,更新最大流
memset(a,0,sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = INF;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i=0;i<g[x].size();i++) {
Edge& e = edges[g[x][i]];
if(!a[e.to]&&e.cap > e.flow) {
p[e.to] = g[x][i]; //记录每次增加流量的路径
a[e.to] = min(a[x],e.cap-e.flow); //求出该道路中所有残量的最小值
Q.push(e.to);
}
}
if(a[t]) break; //到达终点,退出
}
if(!a[t]) break; //终点残量为0,不能再增广,break;
for(int u=t;u != s; u = edges[p[u]].from) {
edges[p[u]].flow += a[t]; //将所求残量加入到该路径中
edges[p[u]^1].flow -= a[t]; //将反向路径减去
}
flow += a[t]; //更新总的最大流
}
return flow;
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
init();
pp.clear(); cnt = 1;
for(int i=0;i<n;i++) {
scanf("%s",s);
if(!pp.count(s)) pp[s] = cnt++;
addedge(pp[s],500,1); //因为插头种类最大400,故将汇点设为500
}
scanf("%d",&m);
for(int i=0;i<m;i++) {
scanf("%s%s",buf,s);
if(!pp.count(s)) pp[s] = cnt++;
addedge(0,pp[s],1); //将源点设为0
}
scanf("%d",&k);
for(int i=0;i<k;i++) {
scanf("%s%s",buf,s);
if(!pp.count(buf)) pp[buf] = cnt++;
if(!pp.count(s)) pp[s] = cnt++;
addedge(pp[buf],pp[s],INF);
}
printf("%d\n",m - maxflow(0,500));
if(T) printf("\n");
}
return 0;
}