斯坦纳树学习笔记
斯坦纳树学习笔记
其实原理不是很懂,不过感性理解一下,咳咳
大概就是有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;
}