zoukankan      html  css  js  c++  java
  • BZOJ 2595: [Wc2008]游览计划(斯坦纳树)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2595

    题意:

    思路:

    学习了一下斯坦纳树,这个我老是想到lol中的斯卡纳。。。

    斯坦纳树和最小生成树是差不多的,斯坦纳树是将给定点连接起来的最小代价,可以额外增加点。主要是通过状压dp和spfa来做。

    f [ i ] [ j ] [ now ] 表示以$(i,j)$为根节点并且状态为now时的最小代价。

    它有两个状态转移方程:

    f[i][j][now]=min(f[i][j][now],f[i][j][s]+f[i][j][now-s]-a[i][j]) 

    这个是枚举子集,将两颗子树合并起来,要注意的一点就是根节点a[i][j]会重复计算依次,这也就是为什么最后还要减去a[i][j]。

    至于枚举子集的方法就是:

    for(int sub=(state-1)&state;sub;sub=(sub-1)&state)

    另一个状态转移方程就是:

    f[i][j][now]=min(f[i][j][now],f[i'][j'][now]+a[i][j])

    这个就是往上下左右四个方向拓展,因为当一个状态的最优解确定,与它相邻的节点的最优解也就能确定下来。

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<sstream>
      6 #include<vector>
      7 #include<stack>
      8 #include<queue>
      9 #include<cmath>
     10 #include<map>
     11 #include<set>
     12 using namespace std;
     13 typedef long long ll;
     14 typedef pair<int,ll> pll;
     15 const int INF = 0x3f3f3f3f;
     16 const int maxn=100+5;
     17 
     18 int n, m, k;
     19 int a[15][15];
     20 int ans[15][15];
     21 int inq[20005];
     22 int f[15][15][2005];
     23 
     24 int dx[5]={1,-1,0,0};
     25 int dy[5]={0,0,1,-1};
     26 
     27 struct node
     28 {
     29     int i,j,state;
     30 }path[15][15][2005];
     31 
     32 struct point
     33 {
     34     int i,j;
     35 };
     36 
     37 queue<point> Q;
     38 
     39 int c(point x)
     40 {
     41     return (x.i-1)*n+x.j;
     42 }
     43 
     44 void spfa(int state)
     45 {
     46     while (!Q.empty())
     47     {
     48         point x=Q.front(); Q.pop(); inq[c(x)]=0;
     49         for (int k=0;k<4;k++)
     50         {
     51             point y;
     52             y.i=x.i+dx[k],y.j=x.j+dy[k];
     53             if (y.i<1||y.j<1||y.i>n||y.j>m) continue;
     54             if (f[y.i][y.j][state]>f[x.i][x.j][state]+a[y.i][y.j])
     55             {
     56                 f[y.i][y.j][state]=f[x.i][x.j][state]+a[y.i][y.j];
     57                 path[y.i][y.j][state].i=x.i,path[y.i][y.j][state].j=x.j,path[y.i][y.j][state].state=state;
     58                 if (!inq[c(y)])  {Q.push(y),inq[c(y)]=1;}
     59             }
     60         }
     61     }
     62 }
     63 
     64 void dfs(int x, int y, int state)
     65 {
     66     if(!path[x][y][state].state)  return;
     67     ans[x][y]=1;
     68     dfs(path[x][y][state].i,path[x][y][state].j,path[x][y][state].state);
     69     if(path[x][y][state].i==x && path[x][y][state].j==y)
     70         dfs(x,y,state^path[x][y][state].state);
     71 }
     72 
     73 int main()
     74 {
     75     //freopen("in.txt","r",stdin);
     76     while(~scanf("%d%d",&n,&m))
     77     {
     78         k=0;
     79         memset(f,0x3f,sizeof(f));
     80         memset(ans,0,sizeof(ans));
     81         memset(path,0,sizeof(path));
     82         for(int i=1;i<=n;i++)
     83         for(int j=1;j<=m;j++)
     84         {
     85             scanf("%d",&a[i][j]);
     86             if(!a[i][j])  f[i][j][1<<(k++)]=0;
     87         }
     88 
     89         for(int state=1;state<(1<<k);state++)
     90         {
     91             for(int i=1;i<=n;i++)
     92             for(int j=1;j<=m;j++)
     93             {
     94                 for(int sub=(state-1)&state;sub;sub=(sub-1)&state)   //枚举子集,将两个子树合并
     95                 {
     96                     if(f[i][j][state]>f[i][j][sub]+f[i][j][state-sub]-a[i][j])  //将两颗树合并,a[i][j]这个根顶点会多算一次,减掉
     97                     {
     98                         f[i][j][state]=f[i][j][sub]+f[i][j][state-sub]-a[i][j];
     99                         path[i][j][state].i=i,path[i][j][state].j=j,path[i][j][state].state=sub; //路径记录
    100                     }
    101                 }
    102                 if(f[i][j][state]!=INF)
    103                 {
    104                     point p;
    105                     p.i=i, p.j=j;
    106                     Q.push(p); inq[c(p)]=1;
    107                 }
    108             }
    109             spfa(state);
    110         }
    111 
    112         int x,y;
    113         for(int i=1;i<=n;i++)
    114         for(int j=1;j<=m;j++)
    115             if(!a[i][j])  {x=i;y=j;break;}
    116 
    117         printf("%d
    ",f[x][y][(1<<k)-1]);
    118 
    119         dfs(x,y,(1<<k)-1);
    120         for(int i=1;i<=n;i++)
    121         {
    122             for(int j=1;j<=m;j++)
    123             {
    124                 if(!a[i][j])  printf("x");
    125                 else if(ans[i][j])  printf("o");
    126                 else printf("_");
    127             }
    128             printf("
    ");
    129         }
    130     }
    131     return 0;
    132 }
  • 相关阅读:
    给Debian安装Xfce桌面
    【兄弟连ThinkPHP】1、介绍和安装
    CROSS JOIN,NATURAL JOIN
    表的连接查询
    多表查询在数据量非常大的时候性能不好,慎用!
    伪列:Oracle显示查询结果前几条记录用rownum<=。去掉重复记录,保留最早录入记录:取出最小ROWID
    其他函数:值为NULL时的默认值NVL,DECODE
    转换函数TO_CHAR,TO_DATE,TO_NUMBER
    50-用Python监听鼠标和键盘事件
    49-Python 安装pythoncom库和pyHook
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/7305563.html
Copyright © 2011-2022 走看看