您的位置:首页 > 其它

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又无法合并子集,所以会有两个方程。

代码的画风和原文差不多

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