您的位置:首页 > 运维架构

POJ 2112 Optimal Milking (二分+最大流/多重匹配) && POJ 2391 Ombrophobic Bovines( 二分+拆点+最大流)

2017-07-23 15:26 495 查看
Optimal Milking

Time Limit: 2000MS Memory Limit: 30000K
Total Submissions: 17940 Accepted: 6421
Case Time Limit: 1000MS
Description

FJ has moved his K (1 <= K <= 30) milking machines out into the cow pastures among the C (1 <= C <= 200) cows. A set of paths of various lengths runs among the cows and the milking machines. The milking machine locations are named by ID numbers 1..K; the cow
locations are named by ID numbers K+1..K+C. 

Each milking point can "process" at most M (1 <= M <= 15) cows each day. 

Write a program to find an assignment for each cow to some milking machine so that the distance the furthest-walking cow travels is minimized (and, of course, the milking machines are not overutilized). At least one legal assignment is possible for all input
data sets. Cows can traverse several paths on the way to their milking machine. 

Input

* Line 1: A single line with three space-separated integers: K, C, and M. 

* Lines 2.. ...: Each of these K+C lines of K+C space-separated integers describes the distances between pairs of various entities. The input forms a symmetric matrix. Line 2 tells the distances from milking machine 1 to each of the other entities; line 3 tells
the distances from machine 2 to each of the other entities, and so on. Distances of entities directly connected by a path are positive integers no larger than 200. Entities not directly connected by a path have a distance of 0. The distance from an entity
to itself (i.e., all numbers on the diagonal) is also given as 0. To keep the input lines of reasonable length, when K+C > 15, a row is broken into successive lines of 15 numbers and a potentially shorter line to finish up a row. Each new row begins on its
own line. 

Output

A single line with a single integer that is the minimum possible total distance for the furthest walking cow. 

Sample Input
2 3 2
0 3 2 1 1
3 0 3 2 0
2 3 0 1 0
1 2 1 0 2
1 0 0 2 0

Sample Output
2


这题最大流跟多重匹配都可以把, 最大流建图也很简单...但是这个题一看就是二分图, 而且无权二分图, 直接多重匹配就好了, 复杂度比网络流少了V。。

题意:

k个机器,每个机器最多服务m头牛。

c头牛,每个牛需要1台机器来服务。

告诉你牛与机器每个之间的直接距离。

问:让所有的牛都被服务的情况下,使走的最远的牛的距离最短,求这个距离

思路:(转)

二分枚举距离,实际距离满足当前枚举距离限制的可以加入这条边。枚举的距离中符合条件的最小值就是答案。

建图过程:

一个超级原点,和每个机器的容量都是m。

一个超级汇点,每头牛和汇点的容量都是1.

机器i与牛j之间的距离如果小于等于当前枚举值mid,连接i,j,容量1或者INF都行.

这样最大流的意义就是能够服务的牛最多是多少,如果最大流等于牛的总数c,表示当前枚举值mid符合条件,同时说明mid值还可能可以更小,更新二分右边界r = mid - 1.

如果小于牛的总数,说明mid偏小,更新二分左边界,l = mid + 1.

机器与牛之间的最短距离可以用floyd预处理出来。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 250;
const int maxv = 1e5;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum;
int n, m, c, a[maxn][maxn], dit[maxn][maxn];
struct node
{
int v, w, next;
}edge[maxv];
void addEdge(int u, int v, int w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = 0;
edge[k].next = head[v];
head[v] = k++;

}
int bfs()
{
memset(d, 0, sizeof(d));
d[s] = 1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
if(u == t) return 1;
q.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == 0)
{
d[to] = d[u] + 1;
if(to == t) return 1;
q.push(to);
}
}
}
return 0;
}
int dfs(int u, int maxflow)
{
if(u == t) return maxflow;
int ret = 0;
for(int i = cur[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == d[u]+1)
{
int f = dfs(to, min(maxflow-ret, w));
edge[i].w -= f;
edge[i^1].w += f;
ret += f;
if(ret == maxflow) return ret;
}
}
return ret;
}
int Dinic(int x)
{
memset(head, -1, sizeof(head));
k = 0;
for(int i = 1; i <= n; i++)
{
addEdge(i, t, c);
for(int j = n+1; j <= sum; j++)
{
if(a[i][j] <= x)
addEdge(j, i, INF);
}
}
for(int i = n+1; i <= sum; i++)
addEdge(s, i, 1);
int ans = 0;
while(bfs() == 1)
{
memcpy(cur, head, sizeof(head));
ans += dfs(s, INF);
}
return ans;
}
void Foyld()
{
for(int k = 1; k <= sum; k++)
{
for(int i = 1; i <= sum; i++)
{
for(int j = 1; j <= sum; j++)
{
if(a[i][j] > a[i][k]+a[k][j])
{
a[i][j] = a[i][k] + a[k][j];
}
}
}
}
}
int main()
{
while(~scanf("%d%d%d", &n, &m, &c))
{
sum = n+m;
s = 0, t = sum+1, k = 0;
memset(head, -1, sizeof(head));
for(int i = 1; i <= sum; i++)
{
for(int j = 1; j <= sum; j++)
{
scanf("%d", &a[i][j]);
if(i != j && !a[i][j])
a[i][j] = INF;
}
}
Foyld();
int l = 0, r = INF, mid, ans;
while(l <= r)
{
mid = (l+r)/2;
if(Dinic(mid) == m)
ans = mid, r = mid - 1;
else
l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}


其实这题二分图多重匹配更快一点。。。
这题一看就可以弄成二分图, 每个点都有自己的“角色”, 这种许多个牛, 然后有许多个挤奶器, 问能不能全都匹配, 一看就是个匹配问题。。这个限制了右面的点有m的大小,就是多重匹配喽...

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 250;
const int maxv = 1e5;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum;
int n, m, c, a[maxn][maxn], dit[maxn][maxn];
struct node
{
int v, next;
}edge[maxv];
void addEdge(int u, int v)
{
edge[k].v = v;
edge[k].next = head[u];
head[u] = k++;
}
int limit[maxn], book[maxn], match[maxn][maxn], cnt[maxn];
int Find(int x)
{
for(int i = head[x]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
if(book[to]) continue;
book[to] = 1;
if(cnt[to] < limit[to])
{
match[to][cnt[to]++] = x;
return 1;
}
for(int j = 0; j < limit[to]; j++)
{
if(Find(match[to][j]))
{
match[to][j] = x;
return 1;
}
}
}
return 0;
}
int solve(int x)
{
memset(head, -1, sizeof(head));
k = n+1;
for(int i = n+1; i <= sum; i++)
{
for(int j = 1; j <= n; j++)
{
if(a[i][j] <= x)
{
addEdge(i, j);
}
}
}
for(int i = 1; i <= n; i++)
limit[i] = c;
memset(cnt, 0, sizeof(cnt));
memset(match, 0, sizeof(match));
int ans = 0;
for(int i = n+1; i <= sum; i++)
{
memset(book, 0, sizeof(book));
ans += Find(i);
}
return ans;
}
void Foyld()
{
for(int k = 1; k <= sum; k++)
{
for(int i = 1; i <= sum; i++)
{
for(int j = 1; j <= sum; j++)
{
if(a[i][j] > a[i][k]+a[k][j])
{
a[i][j] = a[i][k] + a[k][j];
}
}
}
}
}
int main()
{
while(~scanf("%d%d%d", &n, &m, &c))
{
sum = n+m;
s = 0, t = sum+1, k = 0;
memset(head, -1, sizeof(head));
for(int i = 1; i <= sum; i++)
{
for(int j = 1; j <= sum; j++)
{
scanf("%d", &a[i][j]);
if(i != j && !a[i][j])
a[i][j] = INF;
}
}
Foyld();
int l = 0, r = INF, mid, ans;
while(l <= r)
{
mid = (l+r)/2;
if(solve(mid) == m)
ans = mid, r = mid - 1;
else
l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}


Ombrophobic Bovines

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 19066 Accepted: 4138
Description

FJ's cows really hate getting wet so much that the mere thought of getting caught in the rain makes them shake in their hooves. They have decided to put a rain siren on the farm to let them know when rain is approaching. They intend to create a rain evacuation
plan so that all the cows can get to shelter before the rain begins. Weather forecasting is not always correct, though. In order to minimize false alarms, they want to sound the siren as late as possible while still giving enough time for all the cows to get
to some shelter. 

The farm has F (1 <= F <= 200) fields on which the cows graze. A set of P (1 <= P <= 1500) paths connects them. The paths are wide, so that any number of cows can traverse a path in either direction. 

Some of the farm's fields have rain shelters under which the cows can shield themselves. These shelters are of limited size, so a single shelter might not be able to hold all the cows. Fields are small compared to the paths and require no time for cows to traverse. 

Compute the minimum amount of time before rain starts that the siren must be sounded so that every cow can get to some shelter.

Input

* Line 1: Two space-separated integers: F and P 

* Lines 2..F+1: Two space-separated integers that describe a field. The first integer (range: 0..1000) is the number of cows in that field. The second integer (range: 0..1000) is the number of cows the shelter in that field can hold. Line i+1 describes field
i. 

* Lines F+2..F+P+1: Three space-separated integers that describe a path. The first and second integers (both range 1..F) tell the fields connected by the path. The third integer (range: 1..1,000,000,000) is how long any cow takes to traverse it.

Output

* Line 1: The minimum amount of time required for all cows to get under a shelter, presuming they plan their routes optimally. If it not possible for the all the cows to get under a shelter, output "-1".

Sample Input
3 4
7 2
0 4
2 6
1 2 40
3 2 70
2 3 90
1 3 120


Sample Output
110


Hint

OUTPUT DETAILS: 

In 110 time units, two cows from field 1 can get under the shelter in that field, four cows from field 1 can get under the shelter in field 2, and one cow can get to field 3 and join the cows from that field under the shelter in field 3. Although there are
other plans that will get all the cows under a shelter, none will do it in fewer than 110 time units.

Source

USACO 2005 March Gold

题意:有f个草场,每个草场当前有一定数目的牛在吃草,下雨时它可以让一定数量的牛在这里避雨,f个草场间有m条路连接,每头牛通过一条路从一点到另一点有一定的时间花费,现在要下雨了,农场主发出警报牛就会立即去避雨。现在告诉每个草场的情况,以及m条边的信息。农场主至少需要提前多久发出警报才能保证所有牛都能避雨?如果不是所有牛都能成功避雨输出-1。

思路:这道题需要拆点,我们把原图拆成一个二分图,避免突破最大距离限制的情况,每个点变成两个点,即i变为i‘和i’‘,建立一个源点连接每一个i’,容量为初始每个草场牛的数目,建立一个汇点,所有的i‘’指向汇点,容量为每个草场能容纳的牛的数目。如果两个点i和j连接,则在i’和j‘’以及j‘和i’‘之间建一条路径,容量为INF,可以走无限多的牛。然后这道题就和之前做的一道POJ2112一样了,POJ2112不用拆点,因为它本身就是一个二分图。

接下来就是Floyd处理出任意两点间最短路径,然后二分答案。

拆点原因:

因为POJ2112上建的图中是一个很明显的二分图,也就是左边的点绝对不会跟左边的点去连边的。而本题中就不一样了,任何点之间都可能有边。

会出现这种情况,1->2->3,这是一条链,而1->2最短路为1,2->3为1,1->3为 2,此时如果我们限制最大距离为1的话,建的新图中必然有1->2,2-3, 然后我们就发现问题了,新图中1和3也会间接的连接起来,而我们显然是不想这么让它流的。这就需要拆点了,对每个点x,拆成x'和x'',然后x'和x''之间有一条无限容量的边,这样的话,随便多少牛路过这个点都是可以的,如果i->j这条边符合了距离限制,就加i'->j''  所有的边加完后,建立源点,对所有的x'连边,容量为已经有的牛,汇点的话,就用所有的j''连接汇点,容量就是能容纳的牛的数量。

这样一拆点,就发现之前的问题不复存在了,还是比如1->2->3这个例子,加的边是1’->2'',2'->3'' 不会有流从1流到3去,因为加的每条边都流向了汇点

总结:第一题是个很明显的二分图, 两边的点不会相互流通, 左面的点能到右面哪个点就只能到右面哪个点, 这个点因为不是二分图, 所以会流动, 从而使得我们规定x,他通过点之间的流通,超过了x。。然后我们就要拆点, 注意这里是 入点连到能到达点的出点,给自己也连一条,自己肯定能到自己,这就跟那道只能到相邻地方的题一样了,这个的限制是只能到k以内的点

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
const int maxn = 250;
const int maxv = 1e5;
int head[maxv], cur[maxv], s, t, k, Q[maxn];
int n, m, c;
ll a[maxn][maxn], d[maxv], sum, in[maxn], out[maxn];
struct node
{
int v, next;
ll w;
}edge[maxv];
void init()
{
s = 0, t = n*2 + 1;
for(int i = 0; i <= n; i++)
{
for(int j = 1; j <= n; j++)
a[i][j] = a[j][i] = INF;
}
}
void addEdge(int u, int v, ll w)
{
edge[k].v = v;
edge[k].w = w;
edge[k].next = head[u];
head[u] = k++;
edge[k].v = u;
edge[k].w = 0;
edge[k].next = head[v];
head[v] = k++;

}
int bfs()
{
memset(d, 0, sizeof(d));
d[s] = 1;
int frnt = 0, rear = 0;
Q[rear++] = s;
while(frnt != rear)
{
int u = Q[frnt++];
if(u == t) return 1;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v, w = edge[i].w;
if(w && d[to] == 0)
{
d[to] = d[u] + 1;
if(to == t) return 1;
Q[rear++] = to;
}
}
}
return 0;
}
ll dfs(int u, ll maxflow)
{
if(u == t || !maxflow) return maxflow;
ll ret = 0;
for(int& i = cur[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
ll w = edge[i].w;
if(w && d[to] == d[u]+1)
{
ll f = dfs(to, min(maxflow-ret, w));
edge[i].w -= f;
edge[i^1].w += f;
ret += f;
if(ret == maxflow) return ret;
}
}
return ret;
}
ll Dinic(ll x)
{
memset(head, -1, sizeof(head));
k = 0;
for(int i = 1; i <= n; i++)
{
addEdge(s, i, in[i]);
addEdge(i+n, t, out[i]);
addEdge(i, i+n, INF);
for(int j = 1; j <= n; j++)
{
if(a[i][j] <= x)
addEdge(i, j+n, INF);
}
}
ll ans = 0;
while(bfs() == 1)
{
// memcpy(cur, head, sizeof(head));
for(int i = 0; i <= t; i++)
cur[i] = head[i];
ans += dfs(s, INF);
}
return ans;
}
void Floyd()
{
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(a[i][j] > a[i][k]+a[k][j])
{
a[i][j] = a[i][k] + a[k][j];
}
}
}
}
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
sum = 0;
for(int i = 1; i <= n; i++)
scanf("%lld%lld", &in[i], &out[i]), sum += in[i];
init();
ll x, y, z;
for(int i = 1; i <= m; i++)
{
scanf("%lld%lld%lld", &x, &y, &z);
if(z < a[x][y]) a[x][y] = a[y][x] = z;
}
Floyd();
ll l = 0, r = 1e12, mid, ans = -1;
while(l <= r)
{
mid = (l+r)/2;
if(Dinic(mid) == sum)
r = mid - 1, ans = mid;
else
l = mid + 1;
}
printf("%lld\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: