753 - A Plug for UNIX (最大流或二分图匹配)

紫书上网络流部分的第一道例题,  刚刚学了最大流,还没有理解二分图匹配 , 这里就只说一下我用最大流是怎么做的吧 。

我们可以假想一个源点,一个汇点,然后对于每一个设备的插头,从源点连一条线,对于每个插座,连一条线到汇点,且容量都为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;
}


你可能感兴趣的:(图论,uva,最大流,ACM-ICPC,二分图匹配)