1. 程式人生 > >bzoj2595: [Wc2008]遊覽計劃

bzoj2595: [Wc2008]遊覽計劃

思路:斯坦納樹

斯坦納樹問題就是給你n個點的圖,點或邊上有權值,有k個點是關鍵點

求使k個關鍵點聯通且權值和最小的方案

我們知道最後的結果一定是一棵樹

DP狀態就是f[i][j]表示以i為根,關鍵點聯通性為j(一個二進位制數,1為已聯通,0為未聯通)

那麼轉移有兩種

列舉子集,把兩個子集合並:f[i][j]=f[i][s]+f[i][j-s](s屬於j)

擴展出一個點,f[i][j]=min(f[v][j]+val(v,i))

第一種可以很好的轉移

第二種可能會有環,拿個spfa跑一跑就好了

這題權值在點上,所以第一種轉移時該點權值重複算了一次,要減去

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=11,maxk=11,maxq=105;
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
using namespace std;
int n,m,K,head,tail,cnt,pw[maxk+5],w[maxk],q[maxq+10],f[maxn][maxn][1<<maxk],pre[maxn][maxn][1<<maxk],a[maxn][maxn],inf;
bool bo[maxn][maxn],vis[maxn][maxn];
inline bool in(int x,int y){return x>=0&&x<n&&y>=0&&y<m;}
inline int id(int x,int y){return x*10+y;}
inline int id2(int x,int y,int s){return s*100+x*10+y;}
inline void decode(int sta,int &x,int &y){x=sta/10,y=sta%10;}
inline void decode2(int sta,int &x,int &y,int &s){s=sta/100,x=sta%100/10,y=sta%10;}
 
void init(){
    pw[0]=1;for (int i=1;i<=11;i++) pw[i]=pw[i-1]<<1;
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++)
        for (int j=0;j<m;j++){
            scanf("%d",&a[i][j]);
            if (!a[i][j]) w[K++]=id(i,j);
        }
}
 
void spfa(int s){
    //printf("s=%d\n",s);
    while (head!=tail){
        if (++head>maxq) head=1;
        int sta=q[head],x,y;
        decode(sta,x,y);
        //printf("x=%d y=%d\n",x,y);
        for (int i=0;i<4;i++){
            int nx=x+dx[i],ny=y+dy[i];
            if (!in(nx,ny)) continue;//printf("nx=%d ny=%d\n",nx,ny);
            if (f[nx][ny][s]>f[x][y][s]+a[nx][ny]){
                f[nx][ny][s]=f[x][y][s]+a[nx][ny];
                //printf("ffffffffffffffffffffffffffffff=%d\n",f[nx][ny][s]);
                pre[nx][ny][s]=id2(x,y,s);
                if (!bo[nx][ny]){
                    if (++tail>maxq) tail=1;
                    bo[nx][ny]=1,q[tail]=id(nx,ny);
                }
            }
        }
        bo[x][y]=0;
    }
}
 
void dfs(int i,int s){
    int x,y,nx,ny,t;
    decode(i,x,y);
    //printf("x=%d y=%d s=%d\n",x,y,s);
    vis[x][y]=1;
     
    if (!pre[x][y][s]) return;
    //printf("%d %d\n",id2(x,y,s),pre[x][y][s]);
    decode2(pre[x][y][s],nx,ny,t);
    //printf("px=%d py=%d pt=%d\n",nx,ny,t);
    dfs(id(nx,ny),t);
    if (x==nx&&y==ny) dfs(id(nx,ny),s^t);
}
 
void work(){
    memset(f,63,sizeof(f)),inf=f[0][0][0];
    for (int i=0,x,y;i<K;i++){
        decode(w[i],x,y);
        f[x][y][pw[i]]=0;
    }
    for (int sta=1;sta<pw[K];sta++){
        head=tail=0;
        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++){
                for (int s=sta&(sta-1);s;s=(s-1)&sta)
                    if (f[i][j][sta]>f[i][j][s]+f[i][j][sta^s]-a[i][j]){
                        f[i][j][sta]=f[i][j][s]+f[i][j][sta^s]-a[i][j];
                        pre[i][j][sta]=id2(i,j,s);
                    }
                if (f[i][j][sta]!=inf) q[++tail]=id(i,j),bo[i][j]=1;//printf("x=%d y=%d sta=%d f=%d\n",i,j,sta,f[i][j][sta]);
            }
        spfa(sta);
    }
    int x,y;
    decode(w[0],x,y);
    //printf("xx=%d yy=%d sta=%d\n",x,y,pw[K]-1);
    printf("%d\n",f[x][y][pw[K]-1]);
    int nx,ny,ns;
    decode2(pre[x][y][pw[K]-1],nx,ny,ns);
    //printf("nx=%d ny=%d ns=%d\n",nx,ny,ns);
    dfs(w[0],pw[K]-1);
    for (int i=0;i<n;i++,puts(""))
        for (int j=0;j<m;j++)
            if (!a[i][j]) putchar('x');
            else if (vis[i][j]) putchar('o');
            else putchar('_');
}
 
int main(){
    //freopen("test.in","r",stdin);freopen("test.out","w",stdout);
    init(),work();
    return 0;
}
/*
8 8
1 4 1 3 4 2 4 1
4 3 1 2 0 1 2 3
3 2 1 3 0 3 1 2
2 6 5 0 2 4 1 0
5 1 2 1 3 4 2 5
5 1 3 1 5 0 1 4
5 0 6 1 4 5 3 4
0 2 2 2 3 4 1 1
 
*/