斯坦纳树

最小斯坦纳树,是在无向图中,花费最小的代价,联通给定的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;
}
posted @ 2022-11-14 17:49  半步蒟蒻  阅读(60)  评论(0)    收藏  举报