两种转移:
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\n",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一下,求出答案。