BZOJ 2595 [Wc2008]游览计划
2016-08-12 16:58
351 查看
斯坦纳树DP。
斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小
——百度百科
我是从这里学习的orz: http://www.cnblogs.com/lazycal/archive/2013/08/31/bzoj-2595.html
上文中提到斯坦纳树DP的两个方程
f[st][i]表示连通性至少为st,且经过i点的最小距离
·方程1.f[st][i] = Min{f[s][i] + f[st - s][i]}(s为st的子集)
·方程2.f[st][i] = Min{f[st][j] + w(i,j)}(i,j之间有边相连)
斯坦纳树的重点就在这里
方程1思路的正确性是显然的,方程1能够进行状态(子集)合并,但是缺点是可能有重复的点集
为此,方程2巧妙地依靠最短路算法消去了重复点集(至于怎么消去的,可以考虑状态合并时,总会有至少一个点没有被重复,于是从这个点往外推,就像松弛操作)。但只有方程2又无法合并子集,所以会有两个方程。
代码的画风和原文差不多
斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小
——百度百科
我是从这里学习的orz: http://www.cnblogs.com/lazycal/archive/2013/08/31/bzoj-2595.html
上文中提到斯坦纳树DP的两个方程
f[st][i]表示连通性至少为st,且经过i点的最小距离
·方程1.f[st][i] = Min{f[s][i] + f[st - s][i]}(s为st的子集)
·方程2.f[st][i] = Min{f[st][j] + w(i,j)}(i,j之间有边相连)
斯坦纳树的重点就在这里
方程1思路的正确性是显然的,方程1能够进行状态(子集)合并,但是缺点是可能有重复的点集
为此,方程2巧妙地依靠最短路算法消去了重复点集(至于怎么消去的,可以考虑状态合并时,总会有至少一个点没有被重复,于是从这个点往外推,就像松弛操作)。但只有方程2又无法合并子集,所以会有两个方程。
代码的画风和原文差不多
#include<cstdio> #include<queue> #include<cstring> using namespace std; queue<int> q; bool vis[11*11]; const int INF = 1<<29; int n, m, cnt=0, f[11][11][1<<11], a[11][11], from[11][11][1<<11]; int pack(const int x,const int y){return x*10 + y;} int pack2(const int x,const int y,const int s){return x*100000 + y*10000 + s;} void unpack(const int x,int &i,int &j){i = x/10; j = x%10;} void unpack2(const int x,int &i,int &j,int &s){s = x%10000; j = (x/10000)%10; i = x/100000;} bool update(int x, int y, int s1, int i, int j, int s2, int w) { if(w<f[x][y][s1]) { f[x][y][s1]=w; from[x][y][s1]=pack2(i,j,s2); return true; } return false; } int dx[4]={0,0,1,-1}, dy[4]={1,-1,0,0}; void SPFA(int sta) { while(!q.empty()) { int x, y; unpack(q.front(),x,y); vis[q.front()]=0; q.pop(); for(int i = 0; i < 4; i++) { int nx=x+dx[i], ny=y+dy[i]; if(nx<0||ny<0||nx>=n||ny>=m)continue; if(update(nx,ny,sta,x,y,sta,f[x][y][sta]+a[nx][ny])) if(!vis[pack(nx,ny)]) { vis[pack(nx,ny)]=1; q.push(pack(nx,ny)); } } } } void mark(int x, int y, int s) { if(a[x][y]!=0){a[x][y]=-1;} if(!from[x][y][s])return; int nx, ny, ns; unpack2(from[x][y][s],nx,ny,ns); mark(nx,ny,ns); if(nx==x && ny==y)mark(x,y,s-ns); } void print() { for(int i = 0; i < n; i++, puts("")) for(int j = 0; j < m; j++) { if(!a[i][j])printf("x"); else if(a[i][j]>0)printf("_"); else printf("o"); } } int main() { scanf("%d%d",&n,&m); for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) for(int k = 0, kk=1<<11; k < kk; k++) f[i][j][k]=INF; for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) { scanf("%d",&a[i][j]); if(!a[i][j]) f[i][j][1<<(cnt++)]=0; } int tot_sta = 1<<cnt; for(int sta = 1; sta < tot_sta; 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) update(i,j,sta,i,j,s,f[i][j][s]+f[i][j][sta-s]-a[i][j]); if(f[i][j][sta]<INF) { q.push(pack(i,j)); vis[pack(i,j)]=1; } } } SPFA(sta); } for(int i = 0; i < n; i++) for(int j = 0; j < m;j++) if(!a[i][j]) { printf("%d\n",f[i][j][tot_sta-1]); mark(i,j,tot_sta-1); print(); return 0; } }
相关文章推荐
- BZOJ 2595: [Wc2008]游览计划 斯坦纳树
- BZOJ_2595_[Wc2008]游览计划_斯坦纳树
- bzoj 2595: [Wc2008]游览计划(斯坦纳树)
- 【bzoj2595】[Wc2008]游览计划 斯坦纳树
- BZOJ 2595 WC 2008 游览计划 斯坦纳树
- BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)
- [BZOJ]2595 [WC2008] 游览计划 斯坦纳树
- BZOJ 2595: [Wc2008]游览计划 斯坦纳树
- 【BZOJ】【2595】【WC2008】游览计划
- 【WC2008】bzoj2595 游览计划
- Bzoj2595: [Wc2008]游览计划
- BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树
- 【BZOJ2595】【Wc2008】游览计划、斯坦纳树
- BZOJ2595 [Wc2008]游览计划
- bzoj2595 [Wc2008]游览计划
- BZOJ2595 [Wc2008]游览计划
- BZOJ 2595: [Wc2008]游览计划(斯坦纳树)
- bzoj2595 [Wc2008]游览计划
- 【BZOJ 2595】【WC 2008】游览计划
- BZOJ 2595: [Wc2008]游览计划