• 最小割 D. Behind the Wall Samara University ACM ICPC 2016-2017 Quarterfinal Qualification Contest


    题目链接:http://codeforces.com/gym/101149/problem/D

    题目大意:

    堡垒受到攻击。堡垒是n*m的矩阵,矩阵里刚开始都是平地,然后那个数值表示在当前平地上建一面墙需要a[i][j]的时间。目前我们在位置(r, c),我们找一种方法,把(r,c)全部围起来需要的最短时间?

    思路:拆点,拆成in和out两个,in和out之间的cap就是a[i][j],然后就是简单的建边拉。

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    #pragma comment(linker,"/STACK:102400000,102400000")
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    
    const int maxn = 50 * 50 * 2 + 5;
    const int INF = 0x3f3f3f3f;
    struct Edge {
      int from, to, cap, flow;
    };
    
    struct Dinic {
      int n, m, s, t;        ///节点的个数,边的编号,起点,终点
      vector<Edge> edges;    // 边数的两倍
      vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
      bool vis[maxn];        // BFS使用
      int d[maxn];           // 从起点到i的距离
      int cur[maxn];         // 当前弧指针
      /////////蓝书363
      int inq[maxn];         // 是否在队列中
      int p[maxn];           // 上一条弧
      int a[maxn];           //可改进量
    
      void ClearAll(int n) {
        this->n = n;        ///这个赋值千万不能忘
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
      }
    
      void ClearFlow() {  ///清除流量,例如蓝书368的UVA11248里面的优化,就有通过清除流量来减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
      }
    
      void Reduce() {///直接减少cap,也是减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
      }
    
      void AddEdge(int from, int to, int cap) {
        edges.push_back((Edge){from, to, cap, 0});
        edges.push_back((Edge){to, from, 0, 0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
      }
    
      bool BFS() {///bfs构建层次图
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        vis[s] = 1;
        d[s] = 0;
        while(!Q.empty()) {
          int x = Q.front(); Q.pop();
          for(int i = 0; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if(!vis[e.to] && e.cap > e.flow) {//只考虑残量网络中的弧
              vis[e.to] = 1;
              d[e.to] = d[x] + 1;
              Q.push(e.to);
            }
          }
        }
        return vis[t];
      }
    
      int DFS(int x, int a) {///a表示目前为止,所有弧的最小残量。但是也可以用它来限制最大流量,例如蓝书368LA2957,利用a来保证增量,使得最后maxflow=k
        if(x == t || a == 0) return a;
        int flow = 0, f;
        for(int& i = cur[x]; i < G[x].size(); i++) {//从上次考虑的弧,即已经访问过的就不需要在访问了
          Edge& e = edges[G[x][i]];
          if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
            e.flow += f;
            edges[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;//如果不在这里终止,效率会大打折扣
          }
        }
        return flow;
      }
        /**最大流*/
      int Maxflow(int s, int t) {
        this->s = s; this->t = t;
        int flow = 0;
        while(BFS()) {
          memset(cur, 0, sizeof(cur));
          flow += DFS(s, INF);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流
        }
        return flow;
      }
    
        /**最小割*/
      char ch[maxn][maxn];
      void Mincut(int x, int y) { /// call this after maxflow求最小割,就是S和T中还存在流量的东西
        BFS();///重新bfs一次
        for (int i = 0; i < x; i++)
            for (int j = 0; j < y; j++)
                ch[i][j] = '.';
        int cnt = 0;
        for(int i = 0; i < edges.size(); i++) {
          Edge& e = edges[i];
          if(vis[e.from] && !vis[e.to] && e.cap >= 0 && e.to - e.from == x * y) {///这里和ISAP不一样
              cnt++;
              int nx = e.from / y, ny = e.from - nx * y;
              ch[nx][ny] = 'X';
              //printf("e.from = %d e.to = %d nx = %d ny = %d
    ", e.from, e.to, nx, ny);
          }
        }
        //printf("cnt = %d
    ", cnt);
        for (int i = 0; i < x; i++){
            for (int j = 0; j < y; j++){
                printf("%c", ch[i][j]);
            }
            cout << endl;
        }
    
      }
    
      void debug(){///debug
        for (int i = 0; i < edges.size(); i++){
            printf("u = %d v = %d cap = %d flow = %d
    ", edges[i].from + 1, edges[i].to + 1, edges[i].cap, edges[i].flow);
        }
      }
    };
    Dinic g;
    
    int n, m, a, b;
    int atlas[maxn][maxn];
    
    int dx[] = {1, -1, 0, 0};
    int dy[] = {0, 0, 1, -1};
    int in_id(int x, int y){
        return x * m + y;
    }
    int out_id(int x, int y){
        return n * m + x * m + y;
    }
    
    int main(){
        scanf("%d%d%d%d", &n, &m, &a, &b);
        a--, b--;
        for (int i = 0; i < n; i++){
            for (int j = 0; j < m; j++){
                scanf("%d", &atlas[i][j]);
            }
        }
        int s = in_id(a, b), t = n * m * 2 + 1;
        g.ClearAll(t);
        for (int i = 0; i < n; i++){
            for (int j = 0; j < m; j++){
                if (a == i && b == j) {
                    for (int k = 0; k < 4; k++){
                        int nx = i + dx[k], ny = j + dy[k];
                        if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                        g.AddEdge(in_id(i, j), in_id(nx, ny), INF);
                    }
                    continue;
                }
                else {
                    g.AddEdge(in_id(i, j), out_id(i, j), atlas[i][j]);
                    if (i == 0 || j == 0 || i == n-1 || j == m-1)
                        g.AddEdge(out_id(i, j), t, INF);
                }
                for (int k = 0; k < 4; k++){
                    int nx = i + dx[k], ny = j + dy[k];
                    if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                    if (nx == a && ny == b) continue;
                    g.AddEdge(out_id(i, j), in_id(nx, ny), INF);
                }
            }
        }
        printf("%d
    ", g.Maxflow(s, t));
        g.Mincut(n, m);
        return 0;
    }
    View Code
  • 相关阅读:
    SQL Server和Oracle数据库索引介绍
    ITPUB上一个Oracle面试题
    国服《巫妖王之怒》3.35冰双持新手献礼指南
    WLK奥法输出循环
    flume架构初接触
    密码校验正则表达式(java 环境)
    初学Mahout测试kmeans算法
    身份证校验(java)
    gcc 中 O选项对空函数的优化
    《肖申克的救赎》语录
  • 原文地址:https://www.cnblogs.com/heimao5027/p/6820898.html
Copyright © 2020-2023  润新知