您的位置:首页 > 其它

【BZOJ2595】 [Wc2008]游览计划

2015-01-10 17:26 393 查看

2595: [Wc2008]游览计划

Time Limit: 10 Sec Memory Limit: 256 MBSec Special Judge

Submit: 586 Solved: 212

[Submit][Status]

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

斯坦纳树。

(用spfa来进行dp转移)

论文:SPFA算法的优化及应用

斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。

本题用状压dp。

f[i][j][now]表示根在(i,j)且与他相连的点的状态至少为now(now是一个二进制数,每一位代表一个景点,1表示与(i,j)连通,0表示不连通)的最优解。

dp方程分为两部分。

首先是f[i][j][now]=min(f[i][j][now],f[i][j][s]+f[i][j][now-s]-a[i][j]) ,这一步的转移通过枚举now的子集来转移。

然后是f[i][j][now]=min(f[i][j][now],f[i'][j'][now]+a[i][j]),因为只有某个点的最优值被更新后,才可能去更新他的相邻点,符合spfa的求解过程,用spfa来转移。

本题要求输出方案,记录一下每个点的最优值是从哪一个转移过来的即可。

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define inf 0x3f3f3f3f
using namespace std;
int k,f[15][15][2000],inq[20000],ans[15][15],n,m,a[15][15];
struct point
{
int i,j;
};
struct Record
{
int i,j,now;
}from[15][15][2005];
queue<point> q;
const int d[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int c(point x)
{
return (x.i-1)*n+x.j;
}
void spfa(int now)
{
while (!q.empty())
{
point x=q.front();
q.pop();
inq[c(x)]=0;
for (int i=0;i<4;i++)
{
point y;
y.i=x.i+d[i][0],y.j=x.j+d[i][1];
if (y.i<1||y.j<1||y.i>n||y.j>m) continue;
if (f[y.i][y.j][now]>f[x.i][x.j][now]+a[y.i][y.j])
{
f[y.i][y.j][now]=f[x.i][x.j][now]+a[y.i][y.j];
if (!inq[c(y)])
q.push(y),inq[c(y)]=1;
from[y.i][y.j][now].i=x.i,from[y.i][y.j][now].j=x.j,from[y.i][y.j][now].now=now;
}
}
}
}
void dfs(int x,int y,int now)
{
if (!from[x][y][now].now)return;
ans[x][y]=1;
dfs(from[x][y][now].i,from[x][y][now].j,from[x][y][now].now);
if (from[x][y][now].i==x&&from[x][y][now].j==y)
dfs(x,y,now^from[x][y][now].now);
}
int main()
{
k=0;
scanf("%d%d",&n,&m);
memset(f,0x3f,sizeof(f));
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][1<<(k++)]=0;
}
if (!k)
{
cout<<0<<endl;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
cout<<"_";
cout<<endl;
}
return 0;
}
for (int now=1;now<(1<<k);now++)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
for (int s=now&(now-1);s;s=now&(s-1))
{
if (f[i][j][now]>f[i][j][s]+f[i][j][now-s]-a[i][j])
{
f[i][j][now]=f[i][j][s]+f[i][j][now-s]-a[i][j];
from[i][j][now].i=i,from[i][j][now].j=j,from[i][j][now].now=s;
}
}
if (f[i][j][now]!=inf)
{
point x;
x.i=i,x.j=j;
q.push(x),inq[c(x)]=1;
}
}
spfa(now);
}
int x,y;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (!a[i][j]) {x=i,y=j;break;}
cout<<f[x][y][(1<<k)-1]<<endl;
dfs(x,y,(1<<k)-1);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (!a[i][j]) cout<<"x";
else if (ans[i][j]) cout<<"o";
else cout<<"_";
}
cout<<endl;
}
return 0;
}




感悟:

1.wa是因为n,m写反了

2.spfa转移dp方程!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: