【bzoj2595】[Wc2008]遊覽計劃 斯坦納樹
題目描述
給出一個N×M的方格圖,每個格子有自己權值,權值為0的格子已被選定。現要再選定一些格子,使得所有選定的格子(包括一開始已被選定的格子)四聯通,並且選定的格子的權值之和最小。輸出這個最小權值及一種可行方案。
輸入
第一行有兩個整數,N和 M,描述方塊的數目。
接下來 N行, 每行有 M 個非負整數, 如果該整數為 0, 則該方塊為一個景點;
否則表示控制該方塊至少需要的誌願者數目。 相鄰的整數用 (若幹個) 空格隔開,
行首行末也可能有多余的空格。
輸出
由 N + 1行組成。第一行為一個整數,表示你所給出的方案
中安排的誌願者總數目。
接下來 N行,每行M 個字符,描述方案中相應方塊的情況:
z ‘o’(小寫英文字母o)表示該方塊安排了誌願者;
z ‘x’(小寫英文字母x)表示該方塊是一個景點;
註:請註意輸出格式要求,如果缺少某一行或者某一行的字符數目和要求不
一致(任何一行中,多余的空格都不允許出現) ,都可能導致該測試點不得分。
樣例輸入
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
樣例輸出
6
xoox
___o
___o
xoox
題解
斯坦納樹裸題
但是本題的斯坦納樹是點權,與大多數圖的邊權不同,下邊講解一般的邊權做法,然後再將本題的題解。
斯坦納樹:給出一些點,選出若幹條邊使得這些點連通,求總邊權的最值。
斯坦納樹是NP問題,不存在多項式時間內的解法,求解方法是狀壓dp。
設 $f[i][j]$ 表示選擇若幹條邊,使得狀態為 $i$ 的給定點連通,並且當前可以選擇下一條邊的端點為 $j$ 的最小邊權和。初始狀態 $f[2^k][pos[k]]=0$ ,其中 $pos[k]$ 為第 $k$ 個給定點的編號。
那麽我們對於每個 $i$ 和 $j$ ,首先枚舉 $i$ 的子集 $k$ ,用 $f[k][j]+f[i-k][j]$ 更新 $f[i][j]$ 。
然後再考慮同層轉移:如果 $x$ 與 $y$ 邊權為 $z$ ,用 $f[i][x]+z$ 更新 $f[i][y]$ ,用 $f[i][y]$ 更新 $f[i][x]$ 。容易發現這個轉移就是最短路,因此使用堆優化Dijkstra跑一遍得出所有的 $f[i][j]$ 。
最終答案就是 $min\{f[2^p-1][i]\}$
這個dp的理解比較顯然,時間復雜度 $O(3^p·n+2^p·m\log n)$ ,其中 $p$ 是給定點的數目。
那麽對於本題,由於權值在點上,因此枚舉子集轉移時,當前點會重復選擇,需要減去代價。
在同層轉移時,求的就是點權最短路,邊長為目標點的點權。
設 $f[i][j][k]$ 表示選定若幹條邊,使得狀態為 $i$ 的給定點連通,並且當前可以選擇下一條邊的相鄰點為 $(j,k)$ 的最小點權和。初始狀態 $f[2^k][posx[k]][posy[k]]=0$ .
首先枚舉子集轉移:$f[i][j][k]=min_{l\subseteq i}f[l][j][k]+f[i-l][j][k]-a[j][k]$ ,然後同層轉移,與 $(j,k)$ 相鄰的點的 $f$ 加上 $a[j][k]$ 可以轉移到 $f[i][j][k]$ 。
輸出方案的話直接記錄路徑,記錄從哪種方式的哪個狀態轉移過來,dfs一遍即可知道選定的點。
時間復雜度 $O(3^p·nm+2^p·nm\log n)$
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N [1030][11][11] using namespace std; typedef pair<int , int> pr; typedef pair<int , pr> ppr; priority_queue<ppr> q; int a[11][11] , px[11] , py[11] , f N , vis N , opt N , lx N , ly N , flag[11][11]; void dfs(int i , int x , int y) { if(!f[i][x][y]) return; flag[x][y] = 1; if(opt[i][x][y]) dfs(i , lx[i][x][y] , ly[i][x][y]); else dfs(lx[i][x][y] , x , y) , dfs(ly[i][x][y] , x , y); } int main() { int n , m , p = 0 , i , j , k , l , x , y , ans = 1 << 30; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) { for(j = 1 ; j <= m ; j ++ ) { scanf("%d" , &a[i][j]); if(!a[i][j]) px[p] = i , py[p++] = j; } } memset(f , 0x3f , sizeof(f)); for(i = 0 ; i < p ; i ++ ) f[1 << i][px[i]][py[i]] = 0; for(i = 1 ; i < (1 << p) ; i ++ ) { for(j = 1 ; j <= n ; j ++ ) for(k = 1 ; k <= m ; k ++ ) for(l = i ; l ; l = i & (l - 1)) if(f[i][j][k] > f[l][j][k] + f[i ^ l][j][k] - a[j][k]) f[i][j][k] = f[l][j][k] + f[i ^ l][j][k] - a[j][k] , opt[i][j][k] = 0 , lx[i][j][k] = l , ly[i][j][k] = i ^ l; for(j = 1 ; j <= n ; j ++ ) for(k = 1 ; k <= m ; k ++ ) q.push(ppr(-f[i][j][k] , pr(j , k))); while(!q.empty()) { x = q.top().second.first , y = q.top().second.second , q.pop(); if(vis[i][x][y]) continue; vis[i][x][y] = 1; if(x > 1 && f[i][x - 1][y] > f[i][x][y] + a[x - 1][y]) f[i][x - 1][y] = f[i][x][y] + a[x - 1][y] , opt[i][x - 1][y] = 1 , lx[i][x - 1][y] = x , ly[i][x - 1][y] = y , q.push(ppr(-f[i][x - 1][y] , pr(x - 1 , y))); if(x < n && f[i][x + 1][y] > f[i][x][y] + a[x + 1][y]) f[i][x + 1][y] = f[i][x][y] + a[x + 1][y] , opt[i][x + 1][y] = 1 , lx[i][x + 1][y] = x , ly[i][x + 1][y] = y , q.push(ppr(-f[i][x + 1][y] , pr(x + 1 , y))); if(y > 1 && f[i][x][y - 1] > f[i][x][y] + a[x][y - 1]) f[i][x][y - 1] = f[i][x][y] + a[x][y - 1] , opt[i][x][y - 1] = 1 , lx[i][x][y - 1] = x , ly[i][x][y - 1] = y , q.push(ppr(-f[i][x][y - 1] , pr(x , y - 1))); if(y < m && f[i][x][y + 1] > f[i][x][y] + a[x][y + 1]) f[i][x][y + 1] = f[i][x][y] + a[x][y + 1] , opt[i][x][y + 1] = 1 , lx[i][x][y + 1] = x , ly[i][x][y + 1] = y , q.push(ppr(-f[i][x][y + 1] , pr(x , y + 1))); } } for(i = 0 ; i < p ; i ++ ) if(ans > f[(1 << p) - 1][px[i]][py[i]]) ans = f[(1 << p) - 1][px[i]][py[i]] , x = i; printf("%d\n" , ans); dfs((1 << p) - 1 , px[x] , py[x]); for(i = 1 ; i <= n ; i ++ ) { for(j = 1 ; j <= m ; j ++ ) { if(!a[i][j]) printf("x"); else if(flag[i][j]) printf("o"); else printf("_"); } puts(""); } return 0; }
【bzoj2595】[Wc2008]遊覽計劃 斯坦納樹