【BZOJ2595】 [Wc2008]游览计划

2595: [Wc2008]游览计划

Time Limit: 10 Sec   Memory Limit: 256 MBSec   Special Judge
Submit: 586   Solved: 212
[ Submit][ Status]

Description

【BZOJ2595】 [Wc2008]游览计划_第1张图片

【BZOJ2595】 [Wc2008]游览计划_第2张图片

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方程!!!

你可能感兴趣的:(dp,OI,bzoj)