动态规划:状压DP-斯坦纳树

最小生成树是最小斯坦纳树的一种特殊情况 

最小生成树是在给定的点集和边中寻求最短网络使所有点连通 

而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小

BZOJ2595

题意是给定一个棋盘图。告诉你景点的位置

然后让你在一些格子上放置指定数量的志愿者使得景点之间可以连通

问你最少需要派遣多少枚志愿者

典型斯坦纳树问题

令f[i][j][k]表示已经连接的景点的集合为k时,包含点a[i][j]的最小值

集合k的引入标志着这是一种状压DP

即以(i,j)为根时,景点集合为k时的斯坦纳树。然后有两种转移

1.由两个子集合并得到集合k,即f[i][j][k]=f[i][j][x]+f[i][j][y]-a[i][j],x|y=k(用景点枚举子集)

2.由根的转移得到,即f[i][j][k]=f[x][y][k]+a[i][j],其中(i,j)和(x,y)相邻(最短路直接SPFA,同层之间的转移有环?)

  1 #include<cstdio>
  2 #include<queue>
  3 //#include<iostream>
  4 using namespace std;
  5 const int INF=100000000;
  6 const int maxn=15;
  7 const int maxm=15;
  8 int n,m,K;
  9 int bin[25];
 10 bool inq[maxn][maxm],vis[maxn][maxm];
 11 int a[maxn][maxm];
 12 int f[maxn][maxm][1024];
 13 int dx[4]={0,0,1,-1};
 14 int dy[4]={1,-1,0,0};
 15 queue<pair<int,int> > q;
 16 struct Data
 17 {
 18     int fit,sed,thd;
 19 }pre[maxn][maxm][1024];
 20 inline int read()
 21 {
 22     int x=0;char ch=getchar();
 23     while(ch>'9'||ch<'0') ch=getchar();
 24     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
 25     return x;
 26 }
 27 void spfa(int sta)
 28 {
 29     //cout<<"###"<<sta<<endl;
 30     while(!q.empty())
 31     {
 32         int x=q.front().first,y=q.front().second;
 33         inq[x][y]=0;q.pop();
 34         //cout<<x<<" "<<y<<endl;
 35         for(int k=0;k<4;k++)
 36         {
 37             int nx=x+dx[k],ny=y+dy[k];
 38             if(x<1||y<1||x>n||y>m) continue;
 39             if(f[nx][ny][sta]>f[x][y][sta]+a[nx][ny])
 40             {
 41                 f[nx][ny][sta]=f[x][y][sta]+a[nx][ny];
 42                 pre[nx][ny][sta]=(Data){x,y,sta};
 43                 if(!inq[nx][ny])
 44                 {
 45                     q.push(make_pair(nx,ny));
 46                     inq[nx][ny]=1;
 47                 }
 48             }
 49         }
 50     }
 51 }
 52 void dfs(int x,int y,int sta)
 53 {
 54     if(x>INF||pre[x][y][sta].thd==0) return;
 55     vis[x][y]=1;
 56     Data t=pre[x][y][sta];
 57     dfs(t.fit,t.sed,t.thd);
 58     if(t.fit==x&&t.sed==y) dfs(x,y,sta-t.thd);
 59 }
 60 int main()
 61 {
 62     bin[0]=1;
 63     for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
 64     n=read();m=read();
 65     for(int i=1;i<=n;i++)
 66         for(int j=1;j<=m;j++)
 67         {
 68             a[i][j]=read();
 69             if(a[i][j]==0) K++;
 70         }
 71     //令f[i][j][k]表示已经连接的景点的集合为k时
 72     //包含点a[i][j]的最小值
 73     for(int i=1;i<=n;i++)
 74         for(int j=1;j<=m;j++)
 75             for(int k=0;k<bin[K];k++) 
 76                 f[i][j][k]=pre[i][j][k].fit=INF;
 77     K=0;
 78     for(int i=1;i<=n;i++)
 79         for(int j=1;j<=m;j++)
 80             if(a[i][j]==0)
 81                 f[i][j][bin[K]]=0,K++;
 82     //最初始是one-hot,集合中只有自己景点
 83     for(int sta=1;sta<bin[K];sta++)
 84     {
 85          for(int i=1;i<=n;i++)
 86          {
 87              for(int j=1;j<=m;j++)
 88              {
 89                 for(int s=sta&(sta-1);s;s=sta&(s-1))
 90                 {
 91                     int t=f[i][j][s]+f[i][j][sta-s]-a[i][j];
 92                     if(t<f[i][j][sta])
 93                     {
 94                         f[i][j][sta]=t;
 95                         pre[i][j][sta]=(Data){i,j,s};
 96                     }
 97                 }
 98                 if(f[i][j][sta]<INF)
 99                     q.push(make_pair(i,j)),inq[i][j]=1;
100             }
101         }
102         spfa(sta);
103     }
104     int x,y;
105     for(int i=1;i<=n;i++)
106         for(int j=1;j<=m;j++)
107             if(a[i][j]==0)
108             {
109                 x=i;y=j;break;
110             }
111     dfs(x,y,bin[K]-1);
112     printf("%d
",f[x][y][bin[K]-1]);
113     for(int i=1;i<=n;i++)
114     {
115         for(int j=1;j<=m;j++)
116         {
117             if(a[i][j]==0) printf("x");
118             else if(vis[i][j]) printf("o");
119             else printf("_");
120         }
121         puts("");
122     }
123     return 0;
124 }

这个题写完之后有一种莫名的打击感

原文地址:https://www.cnblogs.com/aininot260/p/9466995.html