Luogu P4294 [WC2008]遊覽計劃___斯坦納樹+spfa+狀壓dp

題目大意:

n*m的矩陣,矩陣上有k個景點,對於某個不是景點的點,打通它需要耗費aia_i(打通後經過不需要耗費),問將n個景點連通需要的最小花費,以及打通的一種最優方案。
在這裏插入圖片描述

分析:

好像是斯坦納樹的模板題,
fi,j,sf_{i,j,s}表示已經連通的景點狀態爲ss,這個連通塊包含點(i,j)(i,j)時的最小點權和是多少
然後我們從小到大枚舉狀態ss
然後先枚舉ss的一個子集ss'
看前面的狀態能否更新當前的狀態,
fi,j,s=min(fi,j,s,fi,j,s+fi,j,sxorsai,jf_{i,j,s}=min(f_{i,j,s},f_{i,j,s'}+f_{i,j,sxors'}-a_{i,j}
然後我們對於能被更新的fi,j,sf_{i,j,s}
我們去跑最短路,看能否更新其他的fx,y,sf_{x,y,s}
最後得到的最小的fi,j,tf_{i,j,t}即爲最小花費,tt是景點全部連通時的狀態
然後對於一個方案數,
我們就可以在做狀壓揹包和最短路的時候,
一旦有狀態被更新,我們就存它是怎麼被更新的,
即這個狀態由什麼轉移過來
最後我們對於一個fi,j,t=ansf_{i,j,t}=ans的點我們把它所涉及的點除景點外全部更改成被打通點,dfs即可

代碼:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>

#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)

#define inf 0x3f3f3f3f
#define N 12

using namespace std;

const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};

int os[N][N][1<<N][2], cs[N][N][1<<N][2], dp[N][N][1<<N], fro[N][N][1<<N], a[N][N], C[N][2], num, n, m, tot;
char ans[N][N];
bool vis[N][N];

queue <int> Q1, Q2;

int cde(int x) {
	return (1 << x);
}

bool check(int x, int y) {
    if (x < 1 || x > n || y < 1 || y > m) return 0;
    return 1;
}

void dfs(int x, int y, int s) { 
	if (ans[x][y] != 'x') ans[x][y] = 'o';
	if (fro[x][y][s] == 1) {
		dfs(os[x][y][s][0], os[x][y][s][1], s);
	} if (fro[x][y][s] == 2) {
		dfs(x, y, cs[x][y][s][0]);
		dfs(x, y, cs[x][y][s][1]);
	}
} 

int main() {
    scanf("%d %d", &n, &m);
    rep(i, 1, n)
        rep(j, 1, m) {
            scanf("%d", &a[i][j]);
            if (!a[i][j]) C[++num][0] = i, C[num][1] = j;
    }
    ++num;
	rep(i, 1, n)
        rep(j, 1, m) {
            dp[i][j][1] = inf;
			if (a[i][j]) dp[i][j][1] = a[i][j]; 
            rep(s, 2, cde(num) - 1) dp[i][j][s] = inf;
    }
    rep(i, 1, num - 1) dp[C[i][0]][C[i][1]][cde(i)] = 0;
	rep(s, 1, cde(num) - 1) {
        rep(i, 1, n)
            rep(j, 1, m) {
                rep(t, 1, s - 1) 
				    if ((s & t) == t) 
					   if (dp[i][j][s] > dp[i][j][t] + dp[i][j][s ^ t] - a[i][j]) {
					       dp[i][j][s] = dp[i][j][t] + dp[i][j][s ^ t] - a[i][j];
					       fro[i][j][s] = 2;
					       cs[i][j][s][0] = t;
					       cs[i][j][s][1] = s ^ t;
				       }
                if (dp[i][j][s] != inf) Q1.push(i), Q2.push(j), vis[i][j] = 1; 
             }	
			 while (!Q1.empty()) {
                   int ux = Q1.front(); Q1.pop();
                   int uy = Q2.front(); Q2.pop();                   
                   rep(c, 0, 3) {
                   	   int vx = ux + dx[c];
                   	   int vy = uy + dy[c];
                       if (check(vx, vy) && dp[vx][vy][s] > dp[ux][uy][s] + a[vx][vy]) {
                           dp[vx][vy][s] = dp[ux][uy][s] + a[vx][vy];
                           fro[vx][vy][s] = 1;
                           os[vx][vy][s][0] = ux; 
                           os[vx][vy][s][1] = uy;
						   if (!vis[vx][vy]) Q1.push(vx), Q2.push(vy), vis[vx][vy] = 1;            
                       }
                   }
                   vis[ux][uy] = 0;
             }
     }
     int cdp = 0x7fffffff, asx, asy;
     rep(i, 1, n) 
         rep(j, 1, m) if (cdp > dp[i][j][cde(num) - 1]) { cdp = dp[i][j][cde(num) - 1]; asx = i; asy = j; }
	 rep(i, 1, n) 
         rep(j, 1, m) ans[i][j] = (a[i][j] == 0 ? 'x' : '_');
     dfs(asx, asy, cde(num) - 1);  
     printf("%d\n", cdp);
     rep(i, 1, n) {
         rep(j, 1, m) printf("%c", ans[i][j]); printf("\n");
     }
	 return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章