【BZOJ1294】[SCOI2009]围豆豆(动态规划,状压)

【BZOJ1294】[SCOI2009]围豆豆(动态规划,状压)

题面

BZOJ
洛谷

题解

首先考虑如何判断一个点是否在一个多边形内(不一定是凸的),我们从这个点开始,朝着一个方向画一条射线,看看它和这个多边形的变相交了几次,如果是奇数次那么一定在这个多边形内,否则不在。
这个可以感性理解一下,如果在内部的话,第一次碰到就是出了这个多边形,第二次又是进来,第三次又是出去......而最后总会出去,所以是奇数次。如果不在内部的话,显然就是进去出去是两两配对的,也就是偶数次。
那么我们可以在网格上从每一个豆豆开始向右侧画一条条的射线,那么和射线交的次数决定了这个豆豆是否在内。同时,放置射线和某条边界完全重合导致的不好计算,我们可以认为我们围豆豆的边在方格中线的偏上位置,而豆豆都恰好在格子的中心,这样子计算就要求强制跨越中线才算豆豆和这条线有交点,这样子就不会有问题了。
所以这个时候我们只需要钦定一个起点,设\(f[x][y][S]\)表示当前在点\((x,y)\),并且围住了\(S\)这些豆豆的最小边界长度,最后只需要在再回到起点就可以了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
#define MAX 15
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
char g[MAX][MAX];
int n,m,D,ans=-1e9,v[MAX];
int f[MAX][MAX][1<<9];
int sum[1<<9];
bool vis[MAX][MAX][1<<9];
struct Node{int x,y,S;};
int X[MAX],Y[MAX];
int d[4][2]={1,0,-1,0,0,1,0,-1};
int Get(int fx,int fy,int x,int y,int S)
{
	for(int i=0;i<D;++i)
		if(((fx==X[i]&&x>X[i])||(fx>X[i]&&x<=X[i]))&&y>Y[i])S^=1<<i;
	return S;
}
void SPFA(int x,int y)
{
	queue<Node> Q;memset(f,63,sizeof(f));
	f[x][y][0]=0;Q.push((Node){x,y,0});
	while(!Q.empty())
	{
		int x=Q.front().x,y=Q.front().y,S=Q.front().S;Q.pop();
		for(int i=0;i<4;++i)
		{
			int xx=x+d[i][0],yy=y+d[i][1];
			if(g[xx][yy]!='0')continue;
			int SS=i<2?Get(x,y,xx,yy,S):S;
			if(f[xx][yy][SS]>f[x][y][S]+1)
			{
				f[xx][yy][SS]=f[x][y][S]+1;
				if(!vis[xx][yy][SS])vis[xx][yy][SS]=true,Q.push((Node){xx,yy,SS});
			}
		}
		vis[x][y][S]=false;
	}
	for(int i=0;i<1<<D;++i)
		ans=max(ans,sum[i]-f[x][y][i]);
}
int main()
{
	n=read();m=read();D=read();
	for(int i=0;i<D;++i)v[i]=read();
	memset(g,'#',sizeof(g));
	for(int i=1;i<=n;++i)scanf("%s",g[i]+1),g[i][m+1]='#';
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(g[i][j]>='1'&&g[i][j]<='9')
				X[g[i][j]-49]=i,Y[g[i][j]-49]=j;
	for(int i=0;i<1<<D;++i)
		for(int j=0;j<D;++j)
			if(i&(1<<j))sum[i]+=v[j];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(g[i][j]=='0')
				SPFA(i,j);
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-10-06 16:50  小蒟蒻yyb  阅读(329)  评论(0编辑  收藏  举报