2595: [Wc2008]游览计划
Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 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
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
Sample Output
xoox
___o
___o
xoox
HINT
对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内
Source
斯坦纳树。
(用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方程!!!