zoukankan      html  css  js  c++  java
  • 斯坦纳树(学习笔记)

    学习博客:https://www.cnblogs.com/ECJTUACM-873284962/p/7643445.html

    斯坦纳树:

    给定k个点,要求将k个点联通,且权值和最小

    f[i][s]表示以i为根,当前联通集合为s的最小代价

    转移方程有两层:

    第一层,通过连通状态的子集进行转移

          f[i][s]=min{ f[i][sub]+f[i][s-sub]-重复权值} 

      枚举子集: for(int sub=(s-1)&s;sub;sub=(sub-1)&s)

    第二重,在当前枚举的连通状态下,对该连通状态进行松弛操作

          f[i][s]=min{ f[i][s], f[j][s]+e[i][j] }

          为什么只需对该连通状态进行松弛?因为更后面的连通状态会由先前的连通状态通过第一重转移得到,所以无需对别的连通状态松弛。松弛操作用SPFA即可。

    例题:bzoj2595  WC2008游览计划

    f[i][j][k]表示以a[i][j]为根,联通状态为k的最小代价

    输出方案要记录一下每个状态是从哪个状态转移过来的,然后倒着递归回去找就好了

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> P;
    
    const int maxn = 15;
    
    int n,m,cnt,INF;
    int f[maxn][maxn][1<<maxn],a[maxn][maxn];
    int vis[maxn][maxn];
    
    struct Sta{ int x,y,z; }pre[maxn][maxn][1<<maxn];
    
    int dx[maxn]={1,-1,0,0};
    int dy[maxn]={0,0,1,-1};
    
    queue<P> q;
    void spfa(int sta){
        while(!q.empty()){
            P u=q.front(); q.pop();
            int x=u.first,y=u.second;
            vis[x][y]=0;
            
            for(int i=0;i<4;i++){
                int xx=x+dx[i],yy=y+dy[i];
                if(xx<0||xx>=n||yy<0||yy>=m) continue; 
                if(f[xx][yy][sta]>f[x][y][sta]+a[xx][yy]){
                    f[xx][yy][sta]=f[x][y][sta]+a[xx][yy];
                    pre[xx][yy][sta]=(Sta){x,y,sta};
                    
                    if(!vis[xx][yy]){
                        q.push(P(xx,yy));
                        vis[xx][yy]=1; 
                    }
                }
            }
        }
    }
    
    void dfs(int i,int j,int k){ // 倒序找方案 
        if(pre[i][j][k].z==0) return;
        Sta u=pre[i][j][k];
        int x=u.x,y=u.y,z=u.z;
        vis[x][y]=1;
        
        dfs(x,y,z);
        if(i==x&&j==y) dfs(i,j,k-z);
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;}
    
    int main(){
        n=read(),m=read();
        memset(f,0x3f,sizeof(f));
        INF=f[0][0][0];
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                a[i][j]=read();
                if(!a[i][j]){
                    f[i][j][1<<cnt]=0;
                    cnt++;
                }
            }
        }
        for(int k=0;k<(1<<cnt);++k){ // 是由子集状态转移过来的,所以要放在外层 
            for(int i=0;i<n;++i){
                for(int j=0;j<m;++j){
                    for(int s=(k-1)&k;s;s=(s-1)&k){
                        if(f[i][j][k]>f[i][j][s]+f[i][j][k-s]-a[i][j]){
                            f[i][j][k]=f[i][j][s]+f[i][j][k-s]-a[i][j];
                            pre[i][j][k]=(Sta){i,j,s};
                        }
                    }
                    if(f[i][j][k]!=INF){ q.push(P(i,j)); vis[i][j]=1; }
                }
            } spfa(k); // 每更新完一层就松弛一层 
        }
        int x,y;
        for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(!a[i][j]) x=i,y=j; // 为什么? 不取最小值? 
        
        printf("%d\n",f[x][y][(1<<cnt)-1]);
        dfs(x,y,(1<<cnt)-1); 
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!a[i][j]) printf("x"); 
                else if(vis[i][j]) printf("o");
                else printf("_");
            }printf("\n");
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    在Fragment中保存WebView状态
    Code First下迁移数据库更改
    脚本解决.NET MVC按钮重复提交问题
    1.1C++入门 未完待续。。。
    0.0C语言重点问题回顾
    12F:数字变换
    12G:忍者道具
    12D:迷阵
    12C:未名冰场
    12B:要变多少次
  • 原文地址:https://www.cnblogs.com/tuchen/p/10429072.html
Copyright © 2011-2022 走看看