1. 程式人生 > >bzoj2595 [Wc2008]遊覽計劃 斯坦納樹

bzoj2595 [Wc2008]遊覽計劃 斯坦納樹

Description


在這裡插入圖片描述

在這裡插入圖片描述

Solution


一開始還以為是什麼奇妙的網路流。。

斯坦納樹裸題。所謂斯坦納樹就是求原圖的一個子圖,使得給定點集連通且邊權之和最小。大概是最小生成樹的拓展
設f[i,x]表示給定點集的連通性為i,下一條邊可以從與x相鄰的邊中選的答案,我們可以

  1. 列舉i的子集設為j,f[i,x]=min(f[i,x],f[j,x]+f[i-j,x])
  2. 列舉x的鄰點設為y,f[i,x]=min(f[i,x],f[i,y]+w[x,y])
    第一個轉移可以直接上,第二個轉移可以用最短路鬆弛。注意這裡是一般的模型,權值都在邊上

本題就是f[i,j,x]表示i行j列狀態為x的答案,轉移的時候注意權值在點上就可以了
輸出方案的話每次記錄轉移方式,然後dfs即可

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int INF=0x3f3f3f3f;
const
int N=12; int f[N][N][(1<<N)+5]; int a[N][N],wjp[N][N][(1<<N)+5]; int xx[N][N][(1<<N)+5],yy[N][N][(1<<N)+5]; int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1}; int id[N][N],n,m; bool prt[N][N],vis[N][N]; std:: queue <int> que; void dfs(int x,int y,int now) { prt[x][y]=true; if (
!f[x][y][now]) return ; if (wjp[x][y][now]) dfs(xx[x][y][now],yy[x][y][now],now); else { dfs(x,y,xx[x][y][now]); dfs(x,y,yy[x][y][now]); } } void spfa(int rec) { for (;!que.empty();) { int x=que.front(); que.pop(); int y=que.front(); que.pop(); rep(k,0,3) { int tx=x+dx[k],ty=y+dy[k]; if (!tx||!ty||tx>n||ty>m) continue; if (x&&f[x][y][rec]+a[tx][ty]<f[tx][ty][rec]) { f[tx][ty][rec]=f[x][y][rec]+a[tx][ty]; wjp[tx][ty][rec]=1; xx[tx][ty][rec]=x; yy[tx][ty][rec]=y; if (!vis[tx][ty]) { que.push(tx); que.push(ty); vis[tx][ty]=true; } } } vis[x][y]=false; } } int main(void) { int cnt=0; scanf("%d%d",&n,&m); fill(f,31); rep(i,1,n) rep(j,1,m) { scanf("%d",&a[i][j]); if (!a[i][j]) { cnt++; f[i][j][1<<(cnt-1)]=0; } } rep(now,1,(1<<cnt)-1) { fill(vis,0); rep(i,1,n) rep(j,1,m) { for (int tmp=(now-1)&now;tmp;tmp=(tmp-1)&now) { if (f[i][j][tmp]+f[i][j][now-tmp]-a[i][j]<f[i][j][now]) { wjp[i][j][now]=0; xx[i][j][now]=tmp; yy[i][j][now]=now-tmp; f[i][j][now]=f[i][j][tmp]+f[i][j][now-tmp]-a[i][j]; } } if (f[i][j][now]!=f[0][0][0]) que.push(i),que.push(j),vis[i][j]=true; } spfa(now); } int ans=INF,stx,sty; rep(i,1,n) rep(j,1,m) if (!a[i][j]) { if (f[i][j][(1<<cnt)-1]<ans) { ans=f[i][j][(1<<cnt)-1]; stx=i,sty=j; } } printf("%d\n", ans); dfs(stx,sty,(1<<cnt)-1); rep(i,1,n) { rep(j,1,m) { if (!a[i][j]) putchar('x'); else if (prt[i][j]) putchar('o'); else putchar('_'); } puts(""); } return 0; }