• (好题)POJ3057


    二分+二分图匹配+BFS


    题意:

    墙壁“X”,空区域(都是人)“.”, 门“D”。

    人向门移动通过时视为逃脱,门每秒能出去一个人,人可以上下左右移动,墙阻止移动。

    求最优移动方案下,最后一个人逃脱的最短时间。如果有人无法安全逃脱(比如被墙围困住),则输出“impossible”。
     
    解法1:
    以每个门为起点可以通过BFS来算出每个人逃离的最短路。
    二分答案,判断所有的人能否在时间T内逃脱。
    考虑某一个门,如果能在时间t从该门逃脱的人,应该是距离该门t以内的人,并且其中只有一人能够从该门逃脱。
    每个时间和门的二元组,都能确定一个对应的能够从中逃脱的人的集合,而通过计算这个二元组和人组成的二分图的最大匹配数,我们就能判断所有人是否逃脱。
    会T。
      1 const int dx[4] = {0, 1, 0, -1};
      2 const int dy[4] = {1, 0, -1, 0};
      3 
      4 int X, Y;
      5 char field[MAXN][MAXN];
      6 
      7 vector<int> dX, dY;
      8 vector<int> pX, pY;
      9 int dist[MAXN][MAXN][MAXN][MAXN];
     10 
     11 const int MAXV = 10001;
     12 int V;                //顶点数
     13 vector<int> G[MAXV];  //图的邻接表表示
     14 int match[MAXV];      //所匹配的定点
     15 bool used[MAXV];      // DFS中用到的访问标记
     16 
     17 //添加无向边,注意顶点编号从0开始
     18 void add_edge(int u, int v) {
     19     G[u].push_back(v);
     20     G[v].push_back(u);
     21 }
     22 
     23 //通过DFS寻找增广路
     24 bool dfs(int v) {
     25     used[v] = true;
     26     for (int i = 0; i < G[v].size(); i++) {
     27         int u = G[v][i], w = match[u];
     28         if (w < 0 || !used[w] && dfs(w)) {
     29             match[v] = u;
     30             match[u] = v;
     31             return true;
     32         }
     33     }
     34     return false;
     35 }
     36 
     37 //二分图最大匹配,返回最大匹配数
     38 int Bipartite_Matching() {
     39     int res = 0;
     40     memset(match, -1, sizeof(match));
     41     for (int v = 0; v < V; v++) {
     42         if (match[v] < 0) {
     43             memset(used, 0, sizeof(used));
     44             if (dfs(v)) {
     45                 res++;
     46             }
     47         }
     48     }
     49     return res;
     50 }
     51 
     52 // 二分,这一点非常精妙
     53 bool C(int t) {
     54     // 0~d-1        时间1跑掉的人
     55     // d~2d-1       时间2跑掉的人
     56     // (t-1)d~td-1  时间t跑掉的人
     57     // td~td+p-1    人
     58     int d = dX.size(), p = pY.size();
     59     V = t * d + p;
     60     REP(i, 0, V) G[i].clear();
     61     REP(i, 0, d) REP(j, 0, p) {
     62         int o = dist[dX[i]][dY[i]][pX[j]][pY[j]];
     63         if (o >= 0) {
     64             REP(k, o, t + 1)
     65             add_edge((k - 1) * d + i, t * d + j);
     66         }
     67     }
     68     return Bipartite_Matching() == p;
     69 }
     70 
     71 void bfs(int x, int y, int d[MAXN][MAXN]) {
     72     queue<int> qx, qy;
     73     d[x][y] = 0;
     74     qx.push(x);
     75     qy.push(y);
     76     while (!qx.empty()) {
     77         x = qx.front(), qx.pop();
     78         y = qy.front(), qy.pop();
     79         REP(i, 0, 4) {
     80             int nx = x + dx[i];
     81             int ny = y + dy[i];
     82             if (nx >= 0 && nx < X && ny >= 0 && ny < Y &&
     83                 field[nx][ny] == '.' && d[nx][ny] < 0) {
     84                 d[nx][ny] = d[x][y] + 1;
     85                 qx.push(nx);
     86                 qy.push(ny);
     87             }
     88         }
     89     }
     90 }
     91 
     92 void solve() {
     93     int n = X * Y;
     94 
     95     //跑最短路
     96     REP(i, 0, dX.size()) bfs(dX[i], dY[i], dist[dX[i]][dY[i]]);
     97 
     98     //最短路
     99     int lb = -1, ub = n + 1;
    100     while (ub - lb > 1) {
    101         int mid = (ub + lb) / 2;
    102         C(mid) ? ub = mid : lb = mid;
    103     }
    104 
    105     if (ub > n) {
    106         printf("impossibe
    ");
    107     } else {
    108         printf("%d
    ", ub);
    109     }
    110 }
    111 
    112 int main() {
    113 #ifndef ONLINE_JUDGE
    114     // freopen("input.txt", "r", stdin);
    115 #endif  // !ONLINE_JUDGE
    116     int T = READ();
    117     while (T--) {
    118         dX.clear(), dY.clear();
    119         pX.clear(), pY.clear();
    120         CLR(field), SET(dist);
    121         X = READ();
    122         Y = READ();
    123         REP(i, 0, X) cin >> field[i];
    124         REP(i, 0, X) REP(j, 0, Y) {
    125             if (field[i][j] == 'D') {
    126                 dX.push_back(i);
    127                 dY.push_back(j);
    128             } else if (field[i][j] == '.') {
    129                 pX.push_back(i);
    130                 pY.push_back(j);
    131             }
    132         }
    133         solve();
    134     }
    135     return 0;
    136 }

    解法2:

    在上述解法下,重复的操作是每次二分都会计算一遍匹配,所以T,那么当T增加时,根据增广路的性质,我们只需要不断增加T,每次加1,就可以找出答案。

    这个其实现在也不怎么看懂。

    下面是不同的地方,一样的没放上来

     1 void solve() {
     2     int n = X * Y;
     3 
     4     //跑最短路
     5     REP(i, 0, dX.size()) bfs(dX[i], dY[i], dist[dX[i]][dY[i]]);
     6 
     7     // 加边
     8     int d = dX.size(), p = pX.size();
     9     V = X * Y * d + p;
    10     for (int v = 0; v < V; ++v) G[v].clear();
    11     REP(i, 0, d) REP(j, 0, p) {
    12         int o = dist[dX[i]][dY[i]][pX[j]][pY[j]];
    13         if (o >= 0) REP(k, o, n + 1) add_edge((k - 1) * d + i, n * d + j);
    14     }
    15 
    16     // 匹配
    17     if (p == 0) {
    18         printf("0
    ");
    19         return;
    20     }
    21     int num = 0;  // 匹配数
    22     memset(match, -1, sizeof(match));
    23     for (int v = 0; v < n * d; v++) {
    24         // n*d是节点总数,每个门一个,把所有情况都跑一遍
    25         memset(used, 0, sizeof(used));
    26         if (dfs(v)) {
    27             if (++num == p) {
    28                 printf("%d
    ", v / d + 1);
    29                 return;
    30             }
    31         }
    32     }
    33     printf("impossible
    ");
    34 }
  • 相关阅读:
    js 数组去重求和 (转载)
    表格插件汇总(转载)
    SQL Server 用一张表的数据更新另一张表的数据(转载)
    C#创建DataTable(转载)
    C# DataTable 和List之间相互转换的方法(转载)
    维度表,实体表,事实表之间的关系
    Scala中foldLeft的总结
    Scala集合Map
    从合并两个Map说开去
    UDAF(用户自定义聚合函数)求众数
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/12321063.html
Copyright © 2020-2023  润新知