斯坦纳树学习笔记

斯坦纳树学习笔记

其实原理不是很懂,不过感性理解一下,咳咳

大概就是有m个点,求满足其中指定n个点被覆盖/或者blablabal其他情况的最小值。

(为什么是大概,反正几乎不会考,随便水水算了

可以肯定最优解是树形结构(为什么我也不知道)。

一般是状压,好像还有其他的,

[Apio2013]机器人,先放在这里,到时候再来填坑吧。

所以,设\(dp[x][state]\)为以x为根节点的树达到\(state\)状态下的最小值。

则,有两种转移方式:

\(dp[x][state]= \left\{\begin{matrix}dp[x][i]+dp[x][state\oplus i](i\epsilon state) \\ dp[y][state]+dis[x][y] \end{matrix}\right.\)

根相同可以直接位运算状态转移,

根不同状态相同可以用spfa转移,

最后代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5006,M=10006;
int n,m,qq,p,g,t,cnt=0,head[N],v[N],f[N][64],t1,t2,t3,ans=2e9,inf,w[N];
struct edge{int nxt,to,w;}e[M<<1];
inline void add(int u,int v,int w){e[++cnt].nxt=head[u],e[cnt].to=v,e[cnt].w=w,head[u]=cnt;} 
queue<int> q;
void spfa(int x){
     int tmp;
     while(!q.empty()){
          tmp=q.front(),q.pop();
          for(int i=head[tmp];i;i=e[i].nxt)
              if(f[e[i].to][x]>f[tmp][x]+e[i].w){
                 f[e[i].to][x]=f[tmp][x]+e[i].w;
                 if((x|w[e[i].to])==x&&!v[e[i].to]) v[e[i].to]=1,q.push(e[i].to);
              }
          v[tmp]=0;
     }
}
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T; 
}
int main(){
    freopen("network.in","r",stdin);
    freopen("network.out","w",stdout);
    memset(f,0x3f,sizeof(f)),inf=f[0][0];
    n=read(),m=read(),qq=read(),t=(1<<n)-1;
    for(int i=1;i<=n;++i) t1=read(),add(0,i,t1),add(i,0,t1),w[i]=(1<<(i-1)),f[i][w[i]]=0;
    for(int i=n+1;i<=n+m;++i) t1=read(),add(0,i,t1),add(i,0,t1);
    for(int i=1;i<=qq;++i) t1=read(),t2=read(),t3=read(),add(t1,t2,t3),add(t2,t1,t3);
    f[0][0]=0;
    for(int i=0;i<=t;++i){
        for(int j=i;j;j=(j-1)&i)
            for(int k=0;k<=n+m;++k)
                f[k][i]=min(f[k][i],f[k][j]+f[k][i^j]);
        for(int j=0;j<=n+m;++j) if(f[j][i]<inf) q.push(j),v[j]=1;
        spfa(i);
    }
    printf("%d\n",f[0][t]);
    return 0;
}

例题:游览计划

题意简介:

n*m的矩阵中选每个点有权值,求是选中点连通的最小权值,并输出一种方案。

分析:

使选中点连通,其他连不连通皆可,斯坦纳树。

将选中点状压,每个点与相邻四个点连一条边。

板子题:

#include<bits/stdc++.h>
using namespace std;
const int N=106,M=1106;
int n,m,t,cnt=0,sum=0,head[N],v[N],w[N],f[N][M],pre[N][M][2],inf,ans=2e9,anst=0;
queue<int>q;
struct edge{int nxt,to;}e[M<<1];
inline void add(int u,int v){e[++sum].nxt=head[u],e[sum].to=v,head[u]=sum;} 
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
   return F*T;
}
void spfa(int x){
     int tmp;
     while(!q.empty()){
          tmp=q.front(),q.pop();   
          for(int i=head[tmp];i;i=e[i].nxt)
              if(f[e[i].to][x]>f[tmp][x]+w[e[i].to]){
                 f[e[i].to][x]=f[tmp][x]+w[e[i].to],pre[e[i].to][x][0]=tmp,pre[e[i].to][x][1]=x;
                 if(!v[e[i].to]) v[e[i].to]=1,q.push(e[i].to);
              }
          v[tmp]=0;
     }
}
void dfs(int x,int y){
     if(!y) return;
     v[x]=1;
     if(x==pre[x][y][0]) dfs(pre[x][y][0],y^pre[x][y][1]);
     dfs(pre[x][y][0],pre[x][y][1]);
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            t=(i-1)*m+j,w[t]=read();
            if(i>1) add(t,t-m),add(t-m,t);
            if(j>1) add(t-1,t),add(t,t-1);
        }
    n*=m,memset(f,0x3f,sizeof(f)),inf=f[0][0];
    for(int i=1;i<=n;++i) if(!w[i]) f[i][1<<cnt]=0,++cnt;
    for(int i=1;i<(1<<cnt);++i){
        for(int j=i;j;j=((j-1)&i))
            for(int k=1;k<=n;++k)
                if(f[k][i]>f[k][j]+f[k][i^j]-w[k])
                   f[k][i]=f[k][j]+f[k][i^j]-w[k],pre[k][i][0]=k,pre[k][i][1]=j;
        for(int j=1;j<=n;++j) if(f[j][i]<inf) v[j]=1,q.push(j);
        spfa(i);
        //for(int j=1;j<=n;++j) cout<<j<<" "<<i<<" "<<f[j][i]<<" "<<pre[j][i][0]<<" "<<pre[j][i][1]<<endl;
    }
    memset(v,0,sizeof(v));
    for(int i=1;i<=n;++i) if(ans>f[i][(1<<cnt)-1]) ans=f[i][(1<<cnt)-1],anst=i;
    printf("%d\n",ans);
    dfs(anst,(1<<cnt)-1);
    for(int i=1;i<=n;++i){
        if(!w[i]) printf("x");
        else if(v[i]) printf("o");
        else printf("_");
        if(i%m==0) printf("\n");
    }
    return 0;
}

转载于:https://www.cnblogs.com/ljk123-de-bo-ke/p/11391110.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值