斯坦纳树
最小斯坦纳树,是在无向图中,花费最小的代价,联通给定的k个关键点,组合优化问题
答案一定是一棵树,若存在环,则其中任意一条边可以删去且答案变小
状压dp,设f[i][s]为以i为根的子树包含几何状态s的最小花费,分类讨论转移:
i的度数为1,枚举树上与i相邻的点j,用f[j][s]+f[j][i]转移f[i][s],对每个s进行最短路松弛操作来转移
i的度数>1,划分i的子树,用f[i][t]+f[i][s-t]转移f[i][s],枚举子集来转移
#include<bits/stdc++.h>
using namespace std;
const int N=555;
int n,m,k,h[N],tot,f[N][1<<11],p[N];
struct edge{
int to,next,w;
}e[N<<1];
inline void add(int x,int y,int w){
e[++tot]={y,h[x],w};h[x]=tot;
}
queue<int>q;
bool in[N];
void spfa(int s){
while(!q.empty()){
int x=q.front();
q.pop();
in[x]=0;
for(int i=h[x];i;i=e[i].next){
int y=e[i].to;
if(f[y][s]>f[x][s]+e[i].w){
f[y][s]=f[x][s]+e[i].w;
if(!in[y]){
in[y]=1;
q.push(y);
}
}
}
}
}
signed main(){
cin>>n>>m>>k;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=k;i++){
cin>>p[i];
f[p[i]][1<<i-1]=0;
}
for(int s=1;s<(1<<k);s++){
for(int i=1;i<=n;i++){
for(int t=s&(s-1);t;t=s&(t-1)){
if(t<s/2)break;
f[i][s]=min(f[i][s],f[i][t]+f[i][s^t]);
}
if(f[i][s]<1e9)q.push(i);
}
spfa(s);
}
cout<<f[p[1]][(1<<k)-1];
return 0;
}
二维网格,记录路径
#include<bits/stdc++.h>
using namespace std;
const int N=12,INF=1e9;
int n,m,tot,h[1<<N],a[N][N],f[N][N][1<<N],dx[5]={-1,1,0,0},dy[5]={0,0,-1,1};
bool in[N][N];
struct Pre{
int x,y,s;
}pre[N][N][1<<N];
queue<pair<int,int>>q;
inline void spfa(int st){
while(!q.empty()){
pair<int,int>p=q.front();q.pop();
in[p.first][p.second]=0;
for(int i=0;i<4;i++){
int x=p.first+dx[i],y=p.second+dy[i];
if(x<1||x>n||y<1||y>m)continue;
if(f[x][y][st]>f[p.first][p.second][st]+a[x][y]){
f[x][y][st]=f[p.first][p.second][st]+a[x][y];
pre[x][y][st]={p.first,p.second,st};
if(!in[x][y]){
in[x][y]=1;
q.push({x,y});
}
}
}
}
}
void dfs(int x,int y,int now){
in[x][y]=1;
Pre tmp=pre[x][y][now];
if(!tmp.x&&!tmp.y)return;
dfs(tmp.x,tmp.y,tmp.s);
if(tmp.x==x&&tmp.y==y)dfs(tmp.x,tmp.y,now-tmp.s);
}
int main(){
cin>>n>>m;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
cin>>a[i][j];
if(!a[i][j])f[i][j][1<<tot]=0,tot++;
}
int limit=1<<tot;
for(int s=0;s<limit;s++){
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
for(int sub=s;sub;sub=(sub-1)&s){
if(f[i][j][sub]+f[i][j][s^sub]-a[i][j]<f[i][j][s]){
f[i][j][s]=f[i][j][sub]+f[i][j][s-sub]-a[i][j];
pre[i][j][s]={i,j,sub};
}
}
if(f[i][j][s]<INF)q.push({i,j}),in[i][j]=1;
}
spfa(s);
}
int ax,ay,flag=0;
for(int i=1;i<=n&&!flag;i++)for(int j=1;j<=m;j++)if(!a[i][j]){
ax=i;ay=j;flag=1;break;
}
cout<<f[ax][ay][limit-1]<<'\n';
memset(in,0,sizeof(in));
dfs(ax,ay,limit-1);
for(int i=1;i<=n;i++,puts(""))for(int j=1;j<=m;j++)
if(!a[i][j])putchar('x');
else if(in[i][j])putchar('o');
else putchar('_');
return 0;
}