zoukankan      html  css  js  c++  java
  • 洛谷P4294 【[WC2008]游览计划】

    教练上次课讲了插头dp,然后列出的插头dp题目里有这道题。本来想练练插头dp的结果用更快更简单的斯坦纳树解决了这道题。
    斯坦纳树经典题目。
    斯坦纳树其实并不是很难,一般用来解决在一张无向图上选
    ( ext{m})个点使这( ext{m})个点连通并且始所选边边权最小。

    那么回到本题。
    (f_{i,j,s})表示当前在第( ext{i})( ext{j})列的点,状态为( ext{s})的时候最小权值和。设( ext{s1})代表( ext{s})的子集。
    那么转移方程显而易见。

    [f_{i,j,s} = min(f_{i,j,s},f_{i,s1xors} - a_{i,j}) ]

    这个转移应该不难看懂吧。
    但是这里会出现一个问题:那就是会出现新的节点。
    我们可以用最短路的方式求出下面一个方程:
    ( ext{x,y})为当前的节点,( ext{xa,ya})是新拓展出的与( ext{x,y})相连接的节点。
    则:

    [f_{xa,ya,s} = min(f_{xa,ya,s},f_{x,y,s} +a_{xa,ya}) ]

    然后我们就顺利的解决了这道题。

    My Code:

    #include <bits/stdc++.h>
    
    const int maxn = 20;
    const int maxm = 20;
    const int inf = 0x3f3f3f3f;
    typedef long long ll;
    
    struct Pair {
      int x, y;
      Pair() { x = 0;  y = 0; }
      Pair(int _x,int _y) { x = _x;  y = _y;  }
    };
    
    inline Pair Make_Pair(int x,int y) {
      return Pair(x,y);
    }
    
    template<class T> inline void read(T& res) {
      res = 0;  bool sign = 0;  char ch;
      do ch = getchar(), sign |= ch == '-'; while(!isdigit(ch));
      while(isdigit(ch)) res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
      (sign) && (res = -res);
    }
    
    const int dir[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};
    int n, m, i, j, k, tx, ty;
    int a[maxn][maxn], f[maxn][maxn][1 << 11];
    bool inq[maxn][maxn], ans[maxn][maxn];
    struct state {
      int x, y, S;
      state() { x = y = S = 0; }
      state(int _x,int _y,int _S) { x = _x;  y = _y;  S = _S; }
    } las[maxn][maxn][1 << 11];
    std::queue<Pair> q;
    
    inline void spfa(int s) {
      while(!q.empty()) {
        Pair qwq = q.front();  q.pop();
        int x = qwq.x, y = qwq.y;  inq[x][y] = 0;
        for(int i = 0, xa, ya;i < 4;i++) {
          xa = x + dir[i][0];
          ya = y + dir[i][1];
          if(xa < 1 || xa > n || ya < 1 || ya > m) continue;
          if(f[x][y][s] + a[xa][ya] < f[xa][ya][s]) {
            f[xa][ya][s] = f[x][y][s] + a[xa][ya];
            if(!inq[xa][ya]) q.push(Make_Pair(xa,ya)), inq[xa][ya] = 1;
            las[xa][ya][s] = state(x,y,s);
          }
        }
      }
    }
    
    void dfs(int i,int j,int s) {
      if(!las[i][j][s].S) return;  ans[i][j] = 1;
      state qwq = las[i][j][s];
      int x = qwq.x, y = qwq.y, S = qwq.S;
      dfs(x,y,S);
      if(i == x && j == y) dfs(i,j,s ^ S);
    }
    
    int main() {
      read(n);  read(m);
      int cnt = 0;
      memset(f,0x3f,sizeof(f));
      for(int i = 1;i <= n;i++) {
        for(int j = 1;j <= m;j++) {
          read(a[i][j]);
          if(!a[i][j]) tx = i, ty = j, f[i][j][1 << cnt] = 0, cnt++;
        }
      }
      for(int l = 1, liml = (1 << cnt) - 1;l <= liml;l++) {
        while(!q.empty()) q.pop();
        for(int i = 1;i <= n;i++) {
          for(int j = 1;j <= m;j++) {
            for(int k = l;k;k = l & (k - 1)) {
              if(f[i][j][l] > f[i][j][k] + f[i][j][k ^ l] - a[i][j]) {
                f[i][j][l] = f[i][j][k] + f[i][j][k ^ l] - a[i][j];
                las[i][j][l] = state(i,j,k);
              }
            }
            if(f[i][j][l] != inf) q.push(Make_Pair(i,j)), inq[i][j] = 1;
          }
        }
        spfa(l);
      }
      printf("%d
    ",f[tx][ty][(1 << cnt) - 1]);
      dfs(tx,ty,(1 << cnt) - 1);
      for(int i = 1;i <= n;i++) {
        for(int j = 1;j <= m;j++) {
          if(!a[i][j]) { printf("x");  continue; }
          ans[i][j] ? printf("o") : printf("_");
        }
        puts("");
      }
      return 0;
    }
    
  • 相关阅读:
    WAF绕过方法
    ”非常危险“的Linux命令
    CSRF--花式绕过Referer技巧
    安卓手机的后门控制工具SPADE
    基于RedHat发行的Apache Tomcat本地提权漏洞
    SQL注入--宽字节注入
    MySQL提权
    CF1067D. Computer Game(斜率优化+倍增+矩阵乘法)
    CF1063F. String Journey(后缀数组+线段树)
    BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)
  • 原文地址:https://www.cnblogs.com/Sai0511/p/11170904.html
Copyright © 2011-2022 走看看