• bzoj 2595 [Wc2008]游览计划(斯坦纳树)


    【题目链接】

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

    【题意】

        给定N*M的长方形,选最少权值和的格子使得要求的K个点连通。

    【科普】

        “斯坦纳树”就是包含给定点的最小生成树。   

    【思路】

      那么本题就是求一棵斯坦纳树。

        设f[i][j][S]表示在点(i,j)且与之相连的点的状态为S。

        有两种转移:

            f[i][j][S]<-f[i][j][S’]+f[i][j][S-S’]-a[i][j],合并子集

            f[i][j][S]<-f[i’][j’][S]+a[i][j],相邻点更新

        第一种转移可能包含重点的情况,所以还需要第二种转移方程。

        第一种转移可以直接枚举子集完成转移。

      第二种转移的更新虽然会出现环的情况,但结果一定满足三角形不等式

        f[i][j][S]<=f[i’][j’][S]  +a[i][j]

        所以可以用spfa算法求。

    【代码】

     1 #include<set>
     2 #include<cmath>
     3 #include<queue>
     4 #include<vector>
     5 #include<cstdio>
     6 #include<cstring>
     7 #include<iostream>
     8 #include<algorithm>
     9 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
    10 using namespace std;
    11  
    12 const int N = 11;
    13 const int inf = 0xf0f0f0f;
    14 const int dx[4]={1,-1,0,0};
    15 const int dy[4]={0,0,1,-1};
    16  
    17 int n,m,K,st[N][N];
    18 int vis[N][N],a[N][N],f[N][N][1<<N],pre[N][N][1<<N];
    19  
    20 int pack(int i,int j) { return i*10+j; }
    21 void unpack(int x,int& i,int& j) { i=x/10,j=x%10; }   
    22 int pack2(int i,int j,int st) { return i*100000+j*10000+st; }
    23 void unpack2(int x,int& i,int& j,int& st) { st=x%10000,i=x/100000,j=(x/10000)%10; }
    24  
    25 int upd(int i,int j,int s,int x,int y,int s2,int w) 
    26 {
    27     if(f[i][j][s]>w) return f[i][j][s]=w,pre[i][j][s]=pack2(x,y,s2),1;
    28     return 0;
    29 }
    30  
    31 queue<int> q;
    32 int inq[N*N];
    33 void spfa(int sta) 
    34 {
    35     while(!q.empty()) {
    36         int u=q.front(),i,j; q.pop();
    37         inq[u]=0;
    38         unpack(u,i,j);
    39         FOR(k,0,3) {
    40             int x=i+dx[k],y=j+dy[k],tmp;
    41             if(x<0||x>=n||y<0||y>=m) continue;
    42             if(upd(x,y,sta,i,j,sta,f[i][j][sta]+a[x][y])&&(!inq[tmp=pack(x,y)])) {
    43                 q.push(tmp),inq[tmp]=1;
    44             }
    45         }
    46     }
    47 }
    48 void dfs(int i,int j,int st) 
    49 {
    50     int x,y,nst;
    51     vis[i][j]=1;
    52     if(!pre[i][j][st]) return ;
    53     unpack2(pre[i][j][st],x,y,nst);
    54     dfs(x,y,nst);
    55     if(x==i&&y==j) dfs(x,y,st-nst);
    56 }
    57  
    58 int main()
    59 {
    60     //freopen("trip.in","r",stdin);
    61     //freopen("trip.out","w",stdout);
    62     memset(f,0xf,sizeof(f));
    63     scanf("%d%d",&n,&m);
    64     FOR(i,0,n-1) FOR(j,0,m-1) {
    65         scanf("%d",&a[i][j]);
    66         if(!a[i][j]) st[i][j]=1<<(K++),f[i][j][st[i][j]]=0;
    67     }
    68     int all=(1<<K),tmp;
    69     FOR(sta,1,all-1) {
    70         FOR(i,0,n-1) FOR(j,0,m-1) {
    71             for(int s=sta&(sta-1);s;s=(s-1)&sta)
    72                 upd(i,j,sta,i,j,s,f[i][j][s]+f[i][j][sta-s]-a[i][j]);
    73             if(f[i][j][sta]!=inf) q.push(tmp=pack(i,j)),inq[tmp]=1;
    74         }
    75         spfa(sta);
    76     }
    77     FOR(i,0,n-1) FOR(j,0,m-1) if(!a[i][j]) {
    78         printf("%d
    ",f[i][j][all-1]);
    79         dfs(i,j,all-1);
    80         FOR(ii,0,n-1) {
    81             FOR(jj,0,m-1) {
    82                 if(!a[ii][jj]) putchar('x');
    83                 else if(vis[ii][jj]) putchar('o');
    84                 else putchar('_');
    85             }
    86             puts("");
    87         }
    88         return 0;
    89     }
    90 }
  • 相关阅读:
    反应堆模式
    ABP领域层——仓储(Repositories)
    如何使用ASP.NET Web API OData在Oracle中使用Entity Framework 6.x Code-First方式开发 OData V4 Service
    dapper的Dapper-Extensions用法(一)
    VisualStudio 怎么使用Visual Leak Detector
    Visual Studio Code开发TypeScript
    Topshelf创建Windows服务
    ENode框架初始化
    知已者明(转)
    配置静态监听解决ORA-12514错误的案例(转)
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5299762.html
Copyright © 2020-2023  润新知