1. 程式人生 > 實用技巧 >P4294 [WC2008]遊覽計劃(斯坦納樹)

P4294 [WC2008]遊覽計劃(斯坦納樹)

最小斯坦納樹用於處理聯通集合中指定的點的最小代價,一般來說可以通過其他不屬於集合的點

最小生成樹是特殊的斯坦納樹,它適用於聯通集合中的所有點。

對於斯坦納樹,從兩方面考慮他,首先我們定義f[i][s]表示以i為根,聯通狀態為s的情況

對於更新,從兩個方面考慮,第一個方面,根不變,列舉子集更新當前狀態

第二個方面,子集不變,通過旁邊的點來更新當前根

這兩個的順序不能改變,因為我們要保證在第二種情況更新的時候,當前聯通所處的狀態已經是最小值

對於為什麼第二步更新必須存在但是隻需要更新當前的狀態呢。

有一個合理的解釋是,因為更加後面的狀態一定會從下一次的第一步來列舉更新出來。但是更新完後,當前狀態並不一定是最小值

因為可能在真實的圖上是有路徑重疊的,因此我們可以通過其他額外的不在集合中的點來更新他。

對於第一步,使用狀壓dp,對於第二步,使用spfa來更新,因為第二步是一個三角不等式,可以進行鬆弛操作.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
typedef pair<int,pll> plll;
const int N=1e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7
; int a[20][20]; int f[20][20][1050]; int st[20][20]; struct node{ int a,b; int now; }pre[20][20][1050]; queue<node> q; int n,m; int dx[]={-1,0,1,0}; int dy[]={0,1,0,-1}; void spfa(int state){ while(q.size()){ auto t=q.front(); q.pop(); st[t.a][t.b]=0; int i;
for(i=0;i<4;i++){ int x=t.a+dx[i]; int y=t.b+dy[i]; if(x&&x<=n&&y&&y<=m){ if(f[x][y][state]>f[t.a][t.b][state]+a[x][y]){ f[x][y][state]=f[t.a][t.b][state]+a[x][y]; pre[x][y][state]={t.a,t.b,state}; if(!st[x][y]){ q.push({x,y,state}); st[x][y]=1; } } } } } } void dfs(int a,int b,int state){ if(!a||!b) return ; st[a][b]=1; auto x=pre[a][b][state]; dfs(x.a,x.b,x.now); if(x.a==a&&x.b==b) dfs(x.a,x.b,state-x.now); //因為是從補集轉移,因此另一邊的補集也要查詢 } int main(){ ios::sync_with_stdio(false); cin>>n>>m; int i,j; int num=0; memset(f,0x3f,sizeof f); for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ cin>>a[i][j]; if(!a[i][j]){ f[i][j][1<<num]=0; num++; } } } int all=(1<<num)-1; for(int state=0;state<1<<num;state++){ for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ for(int s=state;s;s=(s-1)&state){ if(f[i][j][state]>f[i][j][s]+f[i][j][state-s]-a[i][j]){ f[i][j][state]=f[i][j][s]+f[i][j][state-s]-a[i][j]; pre[i][j][state]={i,j,s}; } } if(f[i][j][state]<inf){ st[i][j]=1; q.push({i,j,state}); } } } spfa(state); } int ans=0x3f3f3f3f; int be=0,ed=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(f[i][j][all]<ans){ ans=f[i][j][all]; be=i,ed=j; } } } memset(st,0,sizeof st); dfs(be,ed,all); cout<<ans<<endl; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(!a[i][j]) cout<<"x"; else{ if(st[i][j]) cout<<"o"; else cout<<"_"; } } cout<<endl; } return 0; }
View Code