这是一个 网络中的最大流问题的 经典问题。
题目:
有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;
}