bzoj 2595 [Wc2008]游览计划(斯坦纳树)
2016-03-20 21:36
543 查看
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=2595
【题意】
给定N*M的长方形,选最少权值和的格子使得要求的K个点连通。
【科普】
“斯坦纳树”就是包含给定点的最小生成树。
【思路】
那么本题就是求一棵斯坦纳树。
设f[i][j][S]表示在点(i,j)且与之相连的点的状态为S。
有两种转移:
f[i][j][S]<-f[i][j][S’]+f[i][j][S-S’]-a[i][j],合并子集
f[i][j][S]<-f[i’][j’][S]+a[i][j],相邻点更新
第一种转移可能包含重点的情况,所以还需要第二种转移方程。
第一种转移可以直接枚举子集完成转移。
第二种转移的更新虽然会出现环的情况,但结果一定满足三角形不等式
f[i][j][S]<=f[i’][j’][S] +a[i][j]
所以可以用spfa算法求。
【代码】
http://www.lydsy.com/JudgeOnline/problem.php?id=2595
【题意】
给定N*M的长方形,选最少权值和的格子使得要求的K个点连通。
【科普】
“斯坦纳树”就是包含给定点的最小生成树。
【思路】
那么本题就是求一棵斯坦纳树。
设f[i][j][S]表示在点(i,j)且与之相连的点的状态为S。
有两种转移:
f[i][j][S]<-f[i][j][S’]+f[i][j][S-S’]-a[i][j],合并子集
f[i][j][S]<-f[i’][j’][S]+a[i][j],相邻点更新
第一种转移可能包含重点的情况,所以还需要第二种转移方程。
第一种转移可以直接枚举子集完成转移。
第二种转移的更新虽然会出现环的情况,但结果一定满足三角形不等式
f[i][j][S]<=f[i’][j’][S] +a[i][j]
所以可以用spfa算法求。
【代码】
#include<set> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define FOR(a,b,c) for(int a=(b);a<=(c);a++) using namespace std; const int N = 11; const int inf = 0xf0f0f0f; const int dx[4]={1,-1,0,0}; const int dy[4]={0,0,1,-1}; int n,m,K,st ; int vis ,a ,f [1<<N],pre [1<<N]; int pack(int i,int j) { return i*10+j; } void unpack(int x,int& i,int& j) { i=x/10,j=x%10; } int pack2(int i,int j,int st) { return i*100000+j*10000+st; } void unpack2(int x,int& i,int& j,int& st) { st=x%10000,i=x/100000,j=(x/10000)%10; } int upd(int i,int j,int s,int x,int y,int s2,int w) { if(f[i][j][s]>w) return f[i][j][s]=w,pre[i][j][s]=pack2(x,y,s2),1; return 0; } queue<int> q; int inq[N*N]; void spfa(int sta) { while(!q.empty()) { int u=q.front(),i,j; q.pop(); inq[u]=0; unpack(u,i,j); FOR(k,0,3) { int x=i+dx[k],y=j+dy[k],tmp; if(x<0||x>=n||y<0||y>=m) continue; if(upd(x,y,sta,i,j,sta,f[i][j][sta]+a[x][y])&&(!inq[tmp=pack(x,y)])) { q.push(tmp),inq[tmp]=1; } } } } void dfs(int i,int j,int st) { int x,y,nst; vis[i][j]=1; if(!pre[i][j][st]) return ; unpack2(pre[i][j][st],x,y,nst); dfs(x,y,nst); if(x==i&&y==j) dfs(x,y,st-nst); } int main() { //freopen("trip.in","r",stdin); //freopen("trip.out","w",stdout); memset(f,0xf,sizeof(f)); scanf("%d%d",&n,&m); FOR(i,0,n-1) FOR(j,0,m-1) { scanf("%d",&a[i][j]); if(!a[i][j]) st[i][j]=1<<(K++),f[i][j][st[i][j]]=0; } int all=(1<<K),tmp; FOR(sta,1,all-1) { FOR(i,0,n-1) FOR(j,0,m-1) { for(int s=sta&(sta-1);s;s=(s-1)&sta) upd(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(tmp=pack(i,j)),inq[tmp]=1; } spfa(sta); } FOR(i,0,n-1) FOR(j,0,m-1) if(!a[i][j]) { printf("%d\n",f[i][j][all-1]); dfs(i,j,all-1); FOR(ii,0,n-1) { FOR(jj,0,m-1) { if(!a[ii][jj]) putchar('x'); else if(vis[ii][jj]) putchar('o'); else putchar('_'); } puts(""); } return 0; } }
相关文章推荐
- 扒开系统调用的三层皮——系统调用(20135304刘世鹏)
- [BZOJ 4129]Haruna’s Breakfast
- 通俗易懂讲解SVM
- 第四周项目四(2)
- 列存储中常用的数据压缩算法
- 模态跳转中Controller的从属
- 插入排序
- xml学习笔记
- shell 脚本记录学习
- 浅谈Storm流式处理框架
- Vim的安装配置
- 排序——快速排序(C++)
- 英文歌曲:because of you(因为你)
- UESTC 1040 Great Inversion 逆序数、构造
- Mitmproxy 拦截、mock移动设备网络请求
- 判断一个机器是大端序还是小端序
- 策略模式
- Android——Activity跳转
- SQL SERVER-集合操作
- java构造函数详解