斯坦纳树

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斯坦纳树相关的知识,希望对你有一定的参考价值。

斯坦纳树

就是一个很暴力的东西。考虑要做最小生成树,其中一些点必须选,一些点可选可不选。必选点比较少,可以用状压维护。

按照状压状态从小到大更新,每次先枚举子集更新自己,再跑最短路更新全局。

复杂度\(O(n 3^n)\),感觉特弱智。

WC2008 游览计划

技术图片

对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,216]的范围内

题解

此题就是斯坦纳树板子题,没什么好说的。重点在于理解更新顺序。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read()
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;

template<class T> il T read(T&x)
    return x=read<T>();

using namespace std;
typedef long long LL;

co int N=11,S=1025,INF=0x3f3f3f3f;
int n,m,tot;
int a[N][N],f[N][N][S];
co int dx[4]=-1,1,0,0,dy[4]=0,0,-1,1;
int vis[N][N];
struct nodeint x,y,s;pre[N][N][S];

queue<pair<int,int> > q;
void spfa(int cur)
    while(q.size())
        int x=q.front().first,y=q.front().second;
        q.pop(),vis[x][y]=0;
        for(int i=0;i<4;++i)
            int nx=x+dx[i],ny=y+dy[i]; // edit 1:dy
            if(nx<1||nx>n||ny<1||ny>m) continue;
            if(f[nx][ny][cur]>f[x][y][cur]+a[nx][ny])
                f[nx][ny][cur]=f[x][y][cur]+a[nx][ny];
                pre[nx][ny][cur]=(node)x,y,cur;
                if(!vis[nx][ny]) q.push(make_pair(nx,ny)),vis[nx][ny]=1;
            
        
    

void dfs(int x,int y,int now)
    vis[x][y]=1;
    node t=pre[x][y][now];
    if(!t.x&&!t.y) return;
    dfs(t.x,t.y,t.s);
    if(t.x==x&&t.y==y) dfs(t.x,t.y,now-t.s);

int main()
    read(n),read(m);
    memset(f,0x3f,sizeof f);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
        if(!read(a[i][j])) f[i][j][1<<tot]=0,++tot;
    int lim=(1<<tot)-1;
    for(int sta=0;sta<=lim;++sta)
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
            for(int s=sta;s;s=(s-1)&sta)
                if(f[i][j][s]+f[i][j][sta-s]-a[i][j]<f[i][j][sta])
                    f[i][j][sta]=f[i][j][s]+f[i][j][sta-s]-a[i][j];
                    pre[i][j][sta]=(node)i,j,s;
                
            if(f[i][j][sta]<INF) q.push(make_pair(i,j)),vis[i][j]=1;
        
        spfa(sta);
    
    int ansx,ansy;
    for(int i=1,flag=0;i<=n&&!flag;++i)
        for(int j=1;j<=m;++j)if(!a[i][j])
            ansx=i,ansy=j,flag=1;break;
        
    printf("%d\n",f[ansx][ansy][lim]);
    memset(vis,0,sizeof vis);
    dfs(ansx,ansy,lim);
    for(int i=1;i<=n;++i,puts(""))
        for(int j=1;j<=m;++j)
            if(!a[i][j]) putchar('x');
            else vis[i][j]?putchar('o'):putchar('_');;
        
    return 0;

BZOJ4774 修路

村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。

对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边的权值和。

1 <= d <= 4,2d <= n <= 104,0 <= m <= 104

题解

这题的特殊点不需要两两连通,所以求的是最小森林。

还是考虑状压,\(f(S,i)\)表示状态为\(S\)的点集连成的生成树的根为\(i\)的最小代价,用斯坦纳树做法转移即可。

每次用\(\min_i f(S,i)\)的值更新\(g(S)\),最后把合法状态的\(g\)进行枚举子集转移即可。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read()
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;

template<class T> il T read(T&x)
    return x=read<T>();

using namespace std;
typedef long long LL;

co int N=10000+1,S=1<<8,INF=0x3f3f3f3f;
int n,m,D;
vector<int> to[N],we[N];
int f[S][N],g[S];
bool vis[N];
queue<int> q;
void spfa(int f[])
    while(q.size())
        int x=q.front();
        q.pop(),vis[x]=0;
        for(int i=0;i<(int)to[x].size();++i)
            int y=to[x][i],w=we[x][i];
            if(f[y]>f[x]+w)
                f[y]=f[x]+w;
                if(!vis[y]) q.push(y),vis[y]=1;
            
        
    

il bool check(int s)
    return (s&((1<<D)-1))==(s>>D);

int main()
    read(n),read(m),read(D);
    while(m--)
        int x=read<int>(),y=read<int>(),w=read<int>();
        to[x].push_back(y),we[x].push_back(w);
        to[y].push_back(x),we[y].push_back(w);
    
    memset(f,0x3f,sizeof f),memset(g,0x3f,sizeof g);
    for(int i=1;i<=D;++i)
        f[1<<(i-1)][i]=f[1<<(D+i-1)][n-i+1]=0;
    int lim=(1<<(D<<1))-1;
    for(int i=0;i<=lim;++i)
        for(int j=1;j<=n;++j)
            for(int k=i&(i-1);k;k=(k-1)&i)
                f[i][j]=min(f[i][j],f[k][j]+f[i^k][j]);
            if(f[i][j]<INF) q.push(j),vis[j]=1;
        
        spfa(f[i]);
        for(int j=1;j<=n;++j) g[i]=min(g[i],f[i][j]);
    
    for(int i=0;i<=lim;++i)
        for(int t=(i-1)&i;t;t=(t-1)&i)
            if(check(t)&&check(i^t)) g[i]=min(g[i],g[t]+g[i^t]);
    printf("%d\n",g[lim]<INF?g[lim]:-1);
    return 0;

以上是关于斯坦纳树的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树

WC 2008 观光计划(斯坦纳树)

状压dp斯坦纳树

状压dp斯坦纳树

状压dp斯坦纳树

bzoj4006[JLOI2015]管道连接(斯坦纳树+dp)