Description
是不是平时在手机里玩吃豆豆游戏玩腻了呢?最近 MOKIA 手机上推出了一种新的围豆豆游戏,大家一起来试一试吧。
游戏的规则非常简单,在一个 N×M(N,M≤10)的矩阵方格内分布着 D(D≤9)颗豆子,每颗豆有不同的分值 Vi。游戏者可以选择任意一个方格作为起始格,每次移动可以随意的走到相邻的四个格子,直到最终又回到起始格。最终游戏者的得分为所有被路径围住的豆豆的分值总和减去游戏者移动的步数。
矩阵中某些格子内设有障碍物,任何时刻游戏者不能进入包含障碍物或豆子的格子。游戏者可能的最低得分为 0,即什么都不做。
注意路径包围的概念,即某一颗豆在路径所形成的多边形(可能是含自交的复杂多边形)的内部。请最大化得分。
Input
第一行两个整数 N 和 M,为矩阵的边长。
第二行一个整数 D,为豆子的总个数。
第三行包含 D 个整数 V1 到 VD,分别为每颗豆子的分值。
接着 N 行有一个 N×M 的字符矩阵来描述游戏矩阵状态,0 表示空格, #表示障碍物。而数字 1 到 9 分别表示对应编号的豆子。
Output
仅包含一个整数,为最高可能获得的分值。
Solution
首先,先介绍一下射线法的知识。该知识为计算几何内容,用于判断一个点是否在一个多边形内。
它的主要思想是从这个点向右做一条直线,数数这条射线与多边形的交点有几个。如果有奇数个交点,那么这个点在图形内部;若有偶数个交点,那么它在多边形外部(大多数情况下是这样的,因为还要考虑这条射线经过了多边形的一个内角大于 180° 的顶点的情况。但是在此题中显然可以不用考虑)。利用这个思想,我们可以根据路径经过某个豆豆右侧射线次数的奇偶性来判断是否被围在路径里。阔以参考一下这里。
下面来分析此题。
可以暴力枚举起点。状态的设计受以上启发,不仅需要记录当前的位置,还要记录经过每一个豆豆右射线的奇偶性情况。观察到经过每一个豆子右射线的奇偶性情况时一个 D 位 01 串,不妨将其视为 D 位的二进制数以方便记录。即状态 f[i][j][k] 表示当前坐标 (i,j),并且经过第t个豆豆右射线的奇偶性为 k>>t&1(1 为奇数,0 为偶数)。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=15,M=1050; int n,m,d,f[N][N][M],vis[N][N],in[N][N][M],x[N],y[N],a[N],ans,xx,yy,tz,tq,cross,u,v,w; int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1}; char s; struct data{ int x,y,z; }; queue<data>q; void solve(int fx,int fy){ memset(f,0,sizeof(f)),memset(in,0,sizeof(in)); f[fx][fy][0]=0,q.push((data){fx,fy,0}); while(!q.empty()){ u=q.front().x,v=q.front().y,w=q.front().z,q.pop(); for(int k=0;k<4;k++){ //枚举方向 xx=u+dx[k],yy=v+dy[k]; if(xx<=0||yy<=0||xx>n||yy>m||vis[xx][yy]!=0) continue; tz=w,tq=f[u][v][w]-1; if(k<2){ //只有上下移动才会经过右射线 cross=min(xx,u); for(int i=1;i<=d;i++){ //枚举豆子 if(x[i]!=cross||y[i]>yy) continue; if(tz>>i-1&1) tq-=a[i]; else tq+=a[i]; tz=tz^(1<<(i-1)); //改变经过次数奇偶性 } } if(!in[xx][yy][tz]++) f[xx][yy][tz]=tq,q.push((data){xx,yy,tz}); } } for(int i=0;i<(1<<d);i++) if(in[fx][fy][i]) ans=max(ans,f[fx][fy][i]); } signed main(){ scanf("%lld%lld%lld",&n,&m,&d); for(int i=1;i<=d;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ scanf(" %c",&s); if(s=='#') vis[i][j]=-1; else if(s>='0'&&s<='9') x[s-'0']=i,y[s-'0']=j,vis[i][j]=s-'0'; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(!vis[i][j]) solve(i,j); printf("%lld ",ans); return 0; }