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