您的位置:首页 > 其它

bzoj 2595: [Wc2008]游览计划(斯坦纳树)

2016-05-28 07:57 239 查看

2595: [Wc2008]游览计划

Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special Judge
Submit: 1253  Solved: 574

[Submit][Status][Discuss]

Description





Input

第一行有两个整数,N和 M,描述方块的数目。 

接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;

否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开,

行首行末也可能有多余的空格。

Output

由 N + 1行组成。第一行为一个整数,表示你所给出的方案

中安排的志愿者总数目。 

接下来 N行,每行M 个字符,描述方案中相应方块的情况: 

z  ‘_’(下划线)表示该方块没有安排志愿者; 

z  ‘o’(小写英文字母o)表示该方块安排了志愿者; 

z  ‘x’(小写英文字母x)表示该方块是一个景点; 

注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不

一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。

Sample Input

4 4

0 1 1 0

2 5 5 1

1 5 5 1

0 1 1 0

Sample Output

6

xoox

___o

___o

xoox

HINT

 对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内

Source

Ljcc930提供SPJ

[Submit][Status][Discuss]

题解:斯坦纳树就是包含给定K个节点的最小生成树(貌似也可以生成森林)。

dp[i][j] 表示到达i这个点,当前状态为j ,注意第二维是状压,用来表示给定点是否被选。

枚举子树的形态:dp[ i ][ j ]=min{ dp[ i ][ j ],dp[ i ][ k ]+dp[ i ][ l ] },其中k和l是对j的一个划分。

按照边进行松弛:dp[ i ][ j ]=min{ dp[ i ][ j ],dp[ i' ][ j ]+w[ i ][ i' ] },其中i和i'之间有边相连,利用spfa 进行松弛。

总复杂度为O(n*3^k)

进行spfa的时候只需要对当前层的节点进行spfa就行了,不需要整个图完全松弛一遍,因为更高的层都可以通过枚举子集而变成若干个更低的层,这样一次spfa的复杂度一下就降了下来,变成了O(n)级别
http://endlesscount.blog.163.com/blog/static/821197872012525113427573/  这里有更详细的介绍。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 13
#define LL long long
#define pa pair<int,int>
#define inf 1000000000
using namespace std;
int n,m,a

,vis

,mi
;
int f

[(1<<N)],k1,can

;
struct data{
int x,y,thd;
}pre

[(1<<N)];
int x[10]={0,0,-1,1},y[10]={-1,1,0,0};
queue<pa> q;
void spfa(int sta)
{
while (!q.empty())
{
int nowx=q.front().first; int nowy=q.front().second;
q.pop();
for (int i=0;i<4;i++)
{
int xx=nowx+x[i]; int yy=nowy+y[i];
if (xx<=0||yy<=0||xx>n||yy>m) continue;
if (f[xx][yy][sta]>f[nowx][nowy][sta]+a[xx][yy])
{
f[xx][yy][sta]=f[nowx][nowy][sta]+a[xx][yy];
pre[xx][yy][sta]=(data){nowx,nowy,sta};
if (!can[xx][yy]){
can[xx][yy]=1;
q.push(make_pair(xx,yy));
}
}
}
can[nowx][nowy]=0;
}
}
void dfs(int x,int y,int sta)
{
if (x==inf||pre[x][y][sta].thd==0) return;
vis[x][y]=1;
data t=pre[x][y][sta];
dfs(t.x,t.y,t.thd);
if (t.x==x&&t.y==y)  dfs(x,y,sta-t.thd);
}
int main()
{
scanf("%d%d",&n,&m);
mi[0]=1;
for (int i=1;i<=11;i++) mi[i]=mi[i-1]*2;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0;k<mi[11];k++)
f[i][j][k]=pre[i][j][k].x=inf;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if (!a[i][j]) f[i][j][mi[k1]]=0,k1++;
}
for (int sta=1;sta<mi[k1];sta++)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
for (int s=sta&(sta-1);s;s=sta&(s-1))
{
int t=f[i][j][s]+f[i][j][sta-s]-a[i][j];
if (t<f[i][j][sta])
{
f[i][j][sta]=t;
pre[i][j][sta]=(data){i,j,s};
}
}
if (f[i][j][sta]<inf)
q.push(make_pair(i,j)),can[i][j]=1;
}
spfa(sta);
}
int x,y;
for (int i=1;i<=n;i++)
{
bool pd=false;
for (int j=1;j<=m;j++)
if (!a[i][j]){
x=i; y=j; pd=true; break;
}
if (pd)  break;
}
printf("%d\n",f[x][y][mi[k1]-1]);
dfs(x,y,mi[k1]-1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (!a[i][j]) printf("x");
else if (vis[i][j]) printf("o");
else printf("_");
if (j==m) printf("\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: