您的位置:首页 > 其它

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算法求。

【代码】

#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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: