bzoj2595 [Wc2008]游览计划(斯坦纳树)

本文介绍了一种基于斯坦纳树的算法实现方案,用于解决二维平面上点集连通的最小代价问题。通过状态压缩动态规划的方法,文章详细阐述了如何高效地找到最优解,并附带提供了完整的代码实现。

题目链接

分析:
图中点集连通的最小代价,考虑斯坦纳树

这是一道二维平面上的斯坦纳树
xue微更改一下状态 f[i][j][k] f [ i ] [ j ] [ k ] ,表示根结点是 (i,j) ( i , j ) ,连通状态是 k k 的最小状态

  • f[i][j][k]=min{f[i][j][l]+f[i][j][s]a[i][j]}

    • f[i][j][k]=min{f[x][y][k]+a[i][j]|(i,j)(x,y)} f [ i ] [ j ] [ k ] = m i n { f [ x ] [ y ] [ k ] + a [ i ] [ j ] | ( i , j ) 和 ( x , y ) 相 邻 }
    • 由于要输出一组合法解,所以我们在转移的时候记录一下转移点即可
      最后在构造可行解的时候,我们找到一棵以景点以根的树即可

      tip

      我有一种恍然大明白的感觉,我们用斯坦纳树转移出来的一定是一棵树
      如果求的是一片森林,在斯坦纳完事后还要再状压dp一遍
      不过这道题求的就是一棵树,所以一遍斯坦纳即可

      bzoj上需要SPJ的题评测基本都炸了,dada们随意吧

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      #include<queue>
      
      using namespace std;
      
      const int INF=0x33333333;
      const int N=12;
      const int zz[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
      int n,m,a[N][N],f[N][N][1100],tot=0,mp[N][N];
      struct node{
          int x,y,s;
          node(int xx=0,int yy=0,int ss=0) {x=xx;y=yy;s=ss;}
      };
      node g[N][N][1100];
      struct point{
          int x,y;
          point(int xx=0,int yy=0) {x=xx;y=yy;}
      };
      queue<point> q;
      bool in[N][N];
      
      void spfa(int S) {
          while (!q.empty()) {
              point now=q.front(); q.pop();
              int i=now.x,j=now.y;
              in[i][j]=0;
              for (int d=0;d<4;d++) {
                  int x=i+zz[d][0],y=j+zz[d][1];
                  if (x<1||y<1||x>n||y>m) continue;
                  if (f[x][y][S]>f[i][j][S]+a[x][y]) {
                      f[x][y][S]=f[i][j][S]+a[x][y];
                      g[x][y][S]=node(i,j,S);
                      if (!in[x][y])
                          q.push(point(x,y)),in[x][y]=1;
                  }  
              }
          }
      }
      
      void dp() {
          for (int S=1;S<(1<<tot);S++) {
              for (int s=(S-1)&S;s;s=(s-1)&S)
                  for (int i=1;i<=n;i++) 
                  for (int j=1;j<=m;j++) {
                      if (f[i][j][S]>f[i][j][s]+f[i][j][S^s]-a[i][j])
                      {
                          f[i][j][S]=f[i][j][s]+f[i][j][S^s]-a[i][j];
                          g[i][j][S]=node(i,j,s);
                      }
                  }
              for (int i=1;i<=n;i++)
              for (int j=1;j<=m;j++)
                  if (f[i][j][S]<INF&&!in[i][j])
                      q.push(point(i,j)),in[i][j]=1;
              spfa(S);
          } 
      }
      
      void get(int i,int j,int s) {
          if (i<1||j<1||i>n||j>m) return;
          mp[i][j]=1;
          node t=g[i][j][s];
          if (t.s==s) get(t.x,t.y,t.s);
          else get(t.x,t.y,t.s),get(t.x,t.y,s^t.s);
      }
      
      void solve() {
          int x,y;
          for (int i=1;i<=n;i++)
              for (int j=1;j<=m;j++)
                  if (!a[i][j]) {x=i,y=j;break;}
          printf("%d\n",f[x][y][(1<<tot)-1]); 
          get(x,y,(1<<tot)-1);
          for (int i=1;i<=n;i++) {
              for (int j=1;j<=m;j++) {
                  if (!a[i][j]) printf("x");
                  else if (mp[i][j]) printf("o");
                  else printf("-");
              }   
              printf("\n");
          }   
      }
      
      int main()
      {
          memset(f,0x33,sizeof(f));
      
          scanf("%d%d",&n,&m);
          for (int i=1;i<=n;i++)
              for (int j=1;j<=m;j++) {
                  scanf("%d",&a[i][j]);
                  if (!a[i][j]) f[i][j][1<<tot]=0,tot++;
              }
          for (int i=1;i<=n;i++)
              for (int j=1;j<=m;j++)
                  for (int k=1;k<(1<<tot);k++) g[i][j][k]=node(-1,-1,-1);
          dp();
          solve();
          return 0;
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值