bzoj 2595 斯坦纳树
2015-08-16 22:04
363 查看
题目大意:
选定一些格子保证景点对应的格子通过这些格子连通,保证选定的所有格子对应的权值和最小
这是相当于理解为将所有点形成的最小生成树
这里点的个数很少,所以可以对每一个点进行状态压缩
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之间有边相连)
那么可以看出来大的状态总是跟小的状态有关,那么总是先求出小的状态集合
利用spfa求解所有状态对应的点跑最短路对其他格点进行松弛
我到现在也不知道为什么这样写效率会高
选定一些格子保证景点对应的格子通过这些格子连通,保证选定的所有格子对应的权值和最小
这是相当于理解为将所有点形成的最小生成树
这里点的个数很少,所以可以对每一个点进行状态压缩
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之间有边相连)
那么可以看出来大的状态总是跟小的状态有关,那么总是先求出小的状态集合
利用spfa求解所有状态对应的点跑最短路对其他格点进行松弛
我到现在也不知道为什么这样写效率会高
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; typedef pair<int,int> pii; #define N 11 const int MAXN=1<<N; const int INF = 0x3f3f3f3f; int n , m ; struct Node{ int x , y , s; Node(){} Node(int x , int y , int s):x(x),y(y),s(s){} }; Node pre [MAXN];//用于回溯找上一个节点 int w , dp [MAXN] , dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; bool vis , flag ; queue<pii> que; bool ok(int x , int y){return x>=1&&x<=n&&y>=1&&y<=m;} void spfa(int state) { while(!que.empty()){ pii u = que.front(); que.pop(); int x = u.first , y = u.second; vis[x][y] = false; for(int i=0 ; i<4 ; i++){ int xx = x+dir[i][0] , yy = y+dir[i][1]; if(!ok(xx,yy)) continue; if(dp[xx][yy][state]>dp[x][y][state]+w[xx][yy]){ dp[xx][yy][state]=dp[x][y][state]+w[xx][yy]; pre[xx][yy][state] = Node(x , y , state); if(!vis[xx][yy]) que.push(make_pair(xx , yy)); } } } } void huisu(int x , int y , int s) { flag[x][y] = true; if(pre[x][y][s].s == 0) return; huisu(pre[x][y][s].x , pre[x][y][s].y , pre[x][y][s].s); if(pre[x][y][s].x==x && pre[x][y][s].y==y) huisu(pre[x][y][s].x , pre[x][y][s].y , s-pre[x][y][s].s); } void print() { for(int i=1 ; i<=n ; i++){ for(int j=1 ; j<=m ; j++) if(!w[i][j]) printf("x"); else if(flag[i][j]) printf("o"); else printf("_"); puts(""); } } int main() { // freopen("in.txt" , "r" , stdin); while(~scanf("%d%d" , &n , &m)) { int num = 0; memset(dp , 0x3f , sizeof(dp)); memset(pre , 0 , sizeof(pre)); for(int i=1 ; i<=n ; i++){ for(int j=1 ; j<=m ; j++){ scanf("%d" , &w[i][j]); if(!w[i][j]){ dp[i][j][1<<num] = 0; num++; } } } int ALL_STATE = 1<<num; for(int k=1 ; k<ALL_STATE ; k++){ for(int i=1 ; i<=n ; i++){ for(int j=1 ; j<=m ; j++){ for(int s=(k-1)&k ; s ; s=(s-1)&k){ int tmps = k-s; if(dp[i][j][k]>dp[i][j][s]+dp[i][j][tmps]-w[i][j]){ dp[i][j][k] = dp[i][j][s]+dp[i][j][tmps]-w[i][j]; pre[i][j][k] = Node(i , j , s); } } if(dp[i][j][k]<INF) que.push(make_pair(i , j)) , vis[i][j]=true; } } spfa(k); } memset(flag , 0 , sizeof(flag)); for(int i=1 ; i<=n ; i++) for(int j=1 ; j<=m ; j++){ if(!w[i][j]){ cout<<dp[i][j][ALL_STATE-1]<<endl; huisu(i , j , ALL_STATE-1); print(); return 0; } } } return 0; }
相关文章推荐
- 数据结构和算法系列15 线索二叉树
- c++primer之try语句块和异常处理
- 连接器 ELF格式 装载可执行文件与虚拟进程空间
- python修饰器
- Codeforces554C:Kyoya and Colored Balls(组合数学计算+费马小定理)
- Opencv 三次样条曲线(Cubic Spline)插值
- 数据结构:二叉树(前,中,后,层次)非递归遍历。
- 进程间通信概述
- Opencv 三次样条曲线(Cubic Spline)插值
- 快慢指针及应用
- IOS 字符串中去除特殊符号 stringByTrimmingCharactersInSet 应该用于账号登录等
- C++ 语法
- Jayspt加密外部属性
- 设计模式--原型模式
- Asp.net初识
- 利用Python和goagent代理爬取1024帖子所有图片
- 状态压缩讲解
- 网页设计基础知识总结
- 834 组队【排序】
- jquery-ui.js源码分享