POJ 1087

116 篇文章 2 订阅
32 篇文章 0 订阅

这是一个 网络中的最大流问题的 经典问题。

题目:

有n个插座,m个电器,k种适配器(a,b),表示插座b可以替代插座a

问最少有多少个 电器 充不到电?

这道题 可以转换为 求最多可以多少个 电器设备可插入? maxflow

那么问题的答案就是 m - maxflow


下面就是 如何求最大流 maxflow. 

增广路算法:每次用BFS找一条最短的增广路径,然后沿着这条路径修改流量值(实际修改的是残量网络的边权)。当没有增广路时,算法停止,此时的流就是最大流。

增广路 就是 一条 从源点到汇点的 一条路径。


什么是源点,什么是汇点?

在本题中,建立两个点source,sink,分别表示 源点与汇点,source点分别指向 各个 电器设备,权值为1,表示只有一个电器设备。然后将 各个电器设备 指向各自能插的 插座,权值为1,表示一个电器设备只能 插一个 插座。然后将 插座 与 可以转换的 插座相连,权值为 MAX,表示可以有无数个转换器。最后将 可以进行转换的插座 连向汇点。

问题要求的是,从源点 到 汇点 的 最大流。

#include <iostream>
#include <map>
#include <string>
#include <queue>
using namespace std;

#define MAXV 500
#define MAX 1<<29
#define min(a,b) ((a)<(b)?(a):(b))

map<string,int> rec_dev;    // 给每一个设备或者插座 一个标号,键值为0表示为新边
char str[30],stmp[30];
int rec_dev_sum;   //设备的编号
int res[MAXV][MAXV];  // 构建的图,G=(V,E),res[i][j]表示第i点到第j点
int n,m,k;
int dis[MAXV];
int source,sink,maxflow;  //分别表示 源点、汇点、最大流

int bfs(){
    int k,i;
    queue<int> q;
    memset(dis,-1,sizeof(dis));   //dis数组为标记数组,-1表示未访问
    dis[sink] = 0;      //从汇点开始,搜索
    q.push(sink);
    while(!q.empty()){
        k = q.front();
        q.pop();
        for(i=0;i<MAXV;i++){
            if(dis[i]==-1&&res[i][k]){  //该点未访问过 且 第i点到第k点可达
                dis[i] = dis[k] + 1;
                q.push(i);
            }
        }
        if(k == source)
            return 1;
    }
    return 0;
}

int dfs(int cur,int cp){
    if(cur == sink) return cp;
    int temp = cp,t;
    for(int i=0;i<MAXV&&temp;i++){
        if(dis[i]+1 == dis[cur] && res[cur][i]){
            t = dfs(i,min(res[cur][i],temp));
            res[cur][i] -= t;
            res[i][cur] += t;
            temp -= t;
        }
    }
    return cp-temp;
}

void dinic(){
    maxflow = 0;   //初始化
    while(bfs())    //如果存在增广路
        maxflow += dfs(source,MAX);
}

int main(){
    int i;
    while(cin>>n){
        memset(res,0,sizeof(res));
        rec_dev.clear();
        rec_dev_sum = 1;  //标号从1开始,0用来表示 源点Source
        source = 0;
        sink = MAXV - 1;
        for(i=0;i<n;i++){
            cin>>str;
            rec_dev[str] = rec_dev_sum++;
            res[rec_dev[str]][sink] = 1;  //插座到汇点的值为1
        }
        cin>>m;
        for(i=0;i<m;i++){
            cin>>stmp>>str;
            rec_dev[stmp] = rec_dev_sum++; //给电器设备编号
            if(!rec_dev[str])         //表示这是一个新的插座,加入新边
                rec_dev[str] = rec_dev_sum++;
            res[source][rec_dev[stmp]] = 1;   //源点到电器设备的值为1
            res[rec_dev[stmp]][rec_dev[str]] = 1;  //电器设备到插座的值为1
        }
        cin>>k;
        for(i=0;i<k;i++){
            cin>>str>>stmp;
            if(!rec_dev[str])   //表示这是一个新的插座,加入新边
                rec_dev[str] = rec_dev_sum ++;
            if(!rec_dev[stmp])
                rec_dev[stmp] = rec_dev_sum ++;
            res[rec_dev[str]][rec_dev[stmp]] = MAX;   //转换器 有无数个,所以 值为无穷大
        }
        dinic();   //求最大流算法
        cout<<m-maxflow;    //电器设备总量 - 最大可以充电的设备 = 没有充电的设备
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值