poj2112网络流扩展
- 题目来源:http://poj.org/problem?id=2112
这题在读题的时候根本没意思到这个可以用网络流最大流来做,看了别人博客才知道。╮(╯▽╰)╭我果然还是渣渣
题目部分
- 题意:
农场主有K台机器,有C头牛,每台机器可以服务M头牛,现在农场主要把机器放在牛群之中。
把牛和机器都看成一个点,编号从1开始,编号前1到K是机器,K+1到K+C是牛,给你一个二维矩阵,0代表没有直接相连的路(但是不代表两点之间不能到达,他们可以通过与其他点相连就可以到达)。
题目要求出,最远的牛到达服务这头牛的距离的最小值(服务方式不同最远距离就会不同) - 思路:
(╮(╯▽╰)╭没看出来可以用网络流做吧)
这里要先用Floyd算出点与点之间的距离,这样就可以得到牛与机器之间路径的距离了。
接下来就是开始2分法穷举了,我们把Floyd获得的距离中最长right与最短left的边获取出来,用二分法进行尝试。
把二分法的mid值假设成是最终答案,这样就可以把图中大于mid的边忽略掉,建立一个网络图,源点与机器相连,边的值为每台机器可以服务的M,汇点与牛相连,边的值为1,把机器到牛且边的值小于等于mid的加入到网络流里。
这样如果这个网络流图的最大流的值是牛的总数C那么mid这个值可能是答案,我们缩小搜索范围,right = mid,如果最大流不等于C,则这个值不可能是答案,因为不是所有牛都被服务到,继续缩小搜索范围,left = mid+1,直到left>=right退出二分搜索,此时right必然是最终答案(题目有说一句肯定存在一种每头牛都被服务的方式,所以这个是可行的。我只能解释到这了,语文能力不够咕~~(╯﹏╰)b)
代码部分
主要注意初始化,每次都二分都需要进行构建网络流图。
#include<iostream>
#include<algorithm>
#include<fstream>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
fstream fin("1.txt");
//streambuf *buf = cin.rdbuf(fin.rdbuf());
const int inf = 1 << 29;
const int MAXN = 310;
int k, c, m, n;
int map[MAXN][MAXN];
int network[MAXN][MAXN];
int pre[MAXN];
bool vis[MAXN];
void init()
{
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
map[i][j] = 0;
}
}
}
int getmin(int a, int b)
{
return a < b ? a : b;
}
int getmax(int a, int b)
{
return a > b ? a : b;
}
void builtmap(int limit)
{
int s = n + 1;
int e = n + 2;
for (int i = 0; i <= e; i++)
{
for (int j = 0; j <= e; j++)
{
network[i][j] = 0;
}
}
for (int i = 1; i <= k; i++)//机器和源点连接,边的权值为m,每台机器可服务牛数
{
network[s][i] = m;
}
for (int i = k + 1; i <= n; i++)//牛和汇点连接,边的权值为1
{
network[i][e] = 1;
}
for (int i = 1; i <= k; i++)//满足条件的机器到牛的边
{
for (int j = k + 1; j <= n; j++)
{
if (map[i][j] && map[i][j] <= limit)
{
network[i][j] = 1;
}
}
}
}
int bfs(int s, int e)
{
int temp;
for (int i = 0; i <= e; i++)
{
pre[i] = 0;
vis[i] = false;
}
pre[s] = 0;
vis[s] = true;
queue<int> que;
que.push(s);
bool haveroute = false;
while (!haveroute && !que.empty())
{
temp = que.front();
que.pop();
for (int i = 0; i <= e; i++)
{
if (network[temp][i] && !vis[i])
{
pre[i] = temp;
vis[i] = true;
if (i == e)
{
haveroute = true;
break;
}
que.push(i);
}
}
}
if (!haveroute)
{
return 0;
}
int minflow = inf;
int t = e;
while (pre[t])
{
minflow = getmin(minflow, network[pre[t]][t]);
t = pre[t];
}
t = e;
while (pre[t])
{
network[pre[t]][t] -= minflow;
network[t][pre[t]] += minflow;
t = pre[t];
}
return minflow;
}
int getflow()
{
int s = n + 1;
int e = n + 2;
int ans = 0;
int result;
while (result = bfs(s, e))
{
ans += result;
}
return ans;
}
int main()
{
while (cin >> k >> c >> m)
{
n = k + c;
init();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> map[i][j];
}
}
//floyd求点与点的最小距离
int maxroute = 0;//记录图中最大距离
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
continue;
if (map[i][k] && map[k][j] && (map[i][j] > map[i][k] + map[k][j] || map[i][j] == 0))
map[i][j] = map[i][k] + map[k][j];
}
}
}
//求出最大边
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (map[i][j])
{
maxroute = getmax(maxroute, map[i][j]);
}
}
}
//我这里没求最小边就直接用0来替代
int left = 0, right = maxroute;
int result = maxroute;
int mid, maxflow;
while (left <= right)
{
mid = (left + right) >> 1;
builtmap(mid);
maxflow = getflow();
if (maxflow >= c)
{
result = mid;
right = mid - 1;
}
else
left = mid + 1;
}
cout << result << endl;
}
return 0;
}