[SMOJ2213]停车场

来源:互联网 发布:牛股宝软件下载 编辑:程序博客网 时间:2024/04/20 18:06

首先要发现数据范围是比较小,如果是传统的图论题,一般不会只有这么小。而再联想最近所做的二分图匹配,似乎有些吻合。当然,仅凭这个无法作出判断。

刚读题的时候,我还以为是搜索题。但是仔细分析一下,很多条件和限制都说明无法直接用搜索解决。
最明显的一点,直接搜索无法处理:每辆车可以停进若干停车位,每个车位只能停一辆车。而要求的是一种分配的方案,使所有车中最大的移动时间最小。

看到最小化最大值,自然而然就应该要想到二分。很遗憾的是,比赛的时候,我做到这里所剩的时间已经不多了,也没有用心发掘题目的本质,以致错失正解。
最终要求的是花费时间,那么不妨就二分这个值。显然,假设当前要考虑花费 k 个单位的时间,如果所有车都能在 k 个单位的时间内找到自己的归宿,且相互之间不会冲突,那么我们就认为 k 个单位的时间应当是足够的,可以尝试用更少的时间;否则时间太短了,无法完成任务,应该用更多的时间。

则现在问题的关键转化为:如何判断所有车能否在 k 个单位时间内停入一个各不相同的车位?
首先明确一点,每辆车到各个停车场的距离可以预先算出,因为各辆车的开车过程(只要最终不停在一起)是不会相互影响的,允许多台汽车同时进入某个可通行格子。所以,以每辆车为起点各跑 bfs 即可,数据范围也比较小,这一步不会占用太多时间。
现在回到我们的问题。可以认为,“停车”就是将车与停车场进行匹配的过程,车到一个停车场的距离为 d,等价于它们之间有一条费用为 d 的边,于是可以据此建立二分图。因为要在 k 个时间内停完车,因此费用大于 k 的边就要被放弃,只有小于等于 k 的边才是有效的。
但是,在网络中构图的时候,只需把这些有效边连接的点之间连一条容量为 1 的边即可。换言之,只要在限定时间内能够到达,具体的值,我们并不关心。

这样,就可以把能够在 k 个时间内匹配的车和停车场建立二分图,再加上源和汇,跑一遍最大流,得到二分图的最大匹配。如果最大匹配恰好等于车的数量,就说明所有车可以在 k 个单位时间内成功停车。
需要注意的是对特殊情况的判断。当没有车的时候,相当于所有移动已经完成,则应该输出 0。

参考代码:

#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <queue>using namespace std;typedef pair <int, int> pii;const int MAXN = 200 + 10;const int dx[] = {-1, 1, 0, 0};const int dy[] = {0, 0, -1, 1};const int INF = 0x3f3f3f3f;struct Edge {    Edge *next;    int cap;    int dest;} edges[MAXN * MAXN], *current, *first_edge[MAXN];int n, m, s, t;char matrix[MAXN][MAXN]; int rev[MAXN][MAXN]; //若 (i, j) 为停车场,则 rev[i][j] 为该停车场在 parks 数组中的下标int cnt_cars, cnt_parks, dis[MAXN][MAXN]; //dis[i][j] 为第 j 辆车到第 i 个停车场的距离pii cars[MAXN], parks[MAXN];bool vis[MAXN][MAXN], vis2[MAXN]; //分别为 bfs 和 dfs 服务void bfs(int car_id) { //预处理第 car_id 辆车到停车场的距离    queue < pair <pii, int> > q; memset(vis, false, sizeof vis); vis[cars[car_id].first][cars[car_id].second] = true;    for (q.push(make_pair(make_pair(cars[car_id].first, cars[car_id].second), 0)); !q.empty(); q.pop()) {        pii pos = q.front().first; int step = q.front().second;        for (int i = 0; i < 4; i++) {            int nx = pos.first + dx[i], ny = pos.second + dy[i];            if (nx >= 0 && nx < n && ny >= 0 && ny < m && matrix[nx][ny] != 'X' && !vis[nx][ny]) {                if (matrix[nx][ny] == 'P') dis[rev[nx][ny]][car_id] = step + 1;                vis[nx][ny] = true;                q.push(make_pair(make_pair(nx, ny), step + 1));            }        }    }}Edge *counterpart(Edge *x) {    return edges + ((x - edges) ^ 1);}void insert(int u, int v, int c) {    current -> next = first_edge[u];    current -> cap = c;    current -> dest = v;    first_edge[u] = current ++;}int dfs(int u, int f) {    if (u == t) return f;    if (vis2[u]) return 0; else vis2[u] = true;    for (Edge *p = first_edge[u]; p; p = p -> next)        if (p -> cap)            if (int res = dfs(p -> dest, min(f, p -> cap))) {                p -> cap -= res;                counterpart(p) -> cap += res;                return res;            }    return 0;}bool isok() {    int ans = 0;    while (true) {        memset(vis2, false, sizeof vis2);        if (int res = dfs(s, INF)) ans += res; else break;    }    return ans == cnt_cars; //满流则当前时间足够}int make_graph(int limit) { //根据时间上限建图,并返回所选边中容量最大的(只为二分取初值服务)    current = edges; fill(first_edge, first_edge + t + 1, (Edge*)0); int res = INF;    for (int i = 0; i < cnt_cars; i++) {        insert(s, i + 1, 1); insert(i + 1, s, 0);        for (int j = 0; j < cnt_parks; j++)            if (dis[j][i] <= limit) {                insert(i + 1, j + cnt_cars + 1, 1), insert(j + cnt_cars + 1, i + 1, 0);                res = max(res, dis[j][i]);            }    }    for (int i = 0; i < cnt_parks; i++) insert(i + cnt_cars + 1, t, 1), insert(t, i + cnt_cars + 1, 0);    return res;}int calc() {    if (!cnt_cars) return 0;    s = 0; t = cnt_cars + cnt_parks + 1;    int  l = 0, r = make_graph(INF - 1); //左开右闭    if (!isok()) return -1;    while (l + 1 < r) {        int mid = l + r >> 1; make_graph(mid);        if (isok()) r = mid; else l = mid;    }    return r;}int main(void) {    freopen("2213.in", "r", stdin);    freopen("2213.out", "w", stdout);    scanf("%d%d", &n, &m);    for (int i = 0; i < n; i++) {        scanf("%s", matrix[i]);        for (int j = 0; j < m; j++)            if (matrix[i][j] == 'C') cars[cnt_cars++] = make_pair(i, j);            else if (matrix[i][j] == 'P') { rev[i][j] = cnt_parks; parks[cnt_parks++] = make_pair(i, j); }    }    memset(dis, 0x3f, sizeof dis);    for (int i = 0; i < cnt_cars; i++) bfs(i);//  for (int i = 0; i < cnt_parks; i++) {//      for (int j = 0; j < cnt_cars; j++) printf("%d ", dis[i][j]);//      putchar('\n');//  }    printf("%d\n", calc());    return 0;}


原创粉丝点击