洛谷P4294 【[WC2008]游览计划】

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

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

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

这个转移应该不难看懂吧。
但是这里会出现一个问题:那就是会出现新的节点。
我们可以用最短路的方式求出下面一个方程:
\(\text{x,y}\)为当前的节点,\(\text{xa,ya}\)是新拓展出的与\(\text{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\n",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;
}
posted @ 2019-07-11 16:33  _connect  阅读(124)  评论(0编辑  收藏  举报
Live2D