斯坦纳树

两种转移:

f[x][s] 在点x,当前关键点的连通状态为S

子集合并 f[x][s]=f[x][s']+f[x][s-s'] 出现圈

求出s这一层后,最优可以去更新其他的点 f[x][s]=f[y][s]+dis(x,y) 常用spfa

这样就求出了某些点连同的连通块,直接求出的就是最小生成树

题目:

BZOJ2595: [Wc2008]游览计划

就是裸斯坦纳树,但需要记录一下路径

#include<bits/stdc++.h>
using namespace std;
#define p(x,y) x*m+y+1
int read(){
  int x=0,f=1;char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
  return x*f;
}
#define N 15
#define inf 1e9
int n,m,f[N][N][1030],a[N][N];
bool vis[N*N],an[N][N];
struct point{
  int x,y;
};
int h[]={-1,0,1,0};
int l[]={0,-1,0,1};
queue<point> q;
struct From{
  int x,y,now;
}from[N][N][1030];
void spfa(int noww){
  while(!q.empty()){
    point now=q.front();vis[p(now.x,now.y)]=0;q.pop();
    for(int i=0;i<4;i++){
      int x=now.x+h[i];
      int y=now.y+l[i];
      if(x<0||y<0||x>=n||y>=m)continue;
      if(f[x][y][noww]>f[now.x][now.y][noww]+a[x][y]){
        f[x][y][noww]=f[now.x][now.y][noww]+a[x][y];
        from[x][y][noww].x=now.x;from[x][y][noww].y=now.y;from[x][y][noww].now=noww;
        if(!vis[p(x,y)])vis[p(x,y)]=1,q.push(point{x,y});
      }
    }
  }
}
void dfs(int x,int y,int now){
  if(!from[x][y][now].now)return;
  an[x][y]=1;
  dfs(from[x][y][now].x,from[x][y][now].y,from[x][y][now].now);
  if(from[x][y][now].x==x&&from[x][y][now].y==y)dfs(x,y,now^from[x][y][now].now);
}
int main(){
  n=read();m=read();
  memset(f,127,sizeof(f));
  int K=0;
  for(int i=0;i<n;i++)
    for(int j=0;j<m;j++){
      a[i][j]=read();
      if(a[i][j]==0){
        f[i][j][1<<K]=0;K++;
      }
    }
  int Max_s=(1<<K);
  for(int sta=1,tmp;sta<Max_s;sta++){
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++){
        for(int s=sta&(sta-1);s;s=(s-1)&sta){
          if(f[i][j][sta]>f[i][j][s]+f[i][j][sta-s]-a[i][j]){
            f[i][j][sta]=f[i][j][s]+f[i][j][sta-s]-a[i][j];
            from[i][j][sta].x=i;from[i][j][sta].y=j;from[i][j][sta].now=s;
          }
        }
        if(f[i][j][sta]<inf){vis[p(i,j)]=1;q.push(point{i,j});}
      }
    spfa(sta);
  }
  int x,y;
  for(int i=0;i<n;i++)
    for(int j=0;j<m;j++)
      if(!a[i][j]){x=i;y=j;break;}
  printf("%d
",f[x][y][Max_s-1]);
  dfs(x,y,Max_s-1);
  for(int i=0;i<n;i++){
    for(int j=0;j<m;j++)
      if(!a[i][j])putchar('x');
      else if(an[i][j])putchar('o');
      else putchar('_');
    puts("");
  }
  return 0;
}

(2)模拟赛题目

求对应点对连通的最小权值和,点对个数<=4

先裸的斯坦纳树跑一遍

因为只要求对应的点对连通,所以两个点对之间可能不联通。所以在简单dp一下,求出答案。

原文地址:https://www.cnblogs.com/wjyi/p/5633460.html