算法:
射线法+状压DP+spfa(有搜索的感觉)
做法:
射线法
首先,什么射线法?射线法是解决判断一个点是否在多边形内部的一个算法,可以点击此链接学习,它的主要思想是从这个点向右做一条射线,数数这条射线与四边形的交点有几个。如果有奇数个交点,那么这个点在图形内部;若有偶数个交点,那么它在多边形外部。
针对这道题,多边形的边只是横着或竖着,那么我们做的这条射线如果与多边形的边重合了怎么办呢?首先,我们发现交点数只与竖线有关,我们把每一段竖线想象成一条上闭下开的线段,即一个点的射线与一条竖线的上端相交时,我们记录交点数,但当它与竖线的下端相交时,我们不记录交点数。
这样,我们就可以判断那些点在多边形内部了。
状压DP
最多12个点,很符合状压DP的标准嘛(虽然我还是没想到)。设 f[i][j][s] 表示当前已走到 ( i , j ) 这个点,已经围住了状态为 s 的豆豆(当然只是暂时围住,射线与多边形有奇数个交点,但多边形可能还没有首尾相接)时的最大得分。实际上状态的完全形态是 f[x][y][i][j][s],其中 x,y 表示从 ( x , y ) 这个点开始画多边形,但由于 x,y 不同的状态不能相互更新,所以省略了。
那怎么更新呢?首先当然要枚举起点 ( x , y )了,然后呢?
SPFA
我们借用 spfa 的思想来更新它。每一个 f[i][j][s] 就好像一个个点,而更新他们就像是在求最长路,起点是 f[x][y][0],终点是 f[x][y][111111111111] 。首先由一个状态 f[i][j][s] ,从 ( i , j ) 可以向上下左右四个方向移动,每一次移动,都判断一下它暂时围住了那些豆豆,得到 f[i’][j’][s’],那么 :
f[i′][j′][s′]=max(f[i′][j′][s′],f[i][j][s]+更改的得分)
与 spfa 的不同就是不用记录入队次数(因为每走一步,得分就会 -1,不会陷入正环而无法自拔)。
其实射♂线朝那边和一条线段那边开那边闭都无所谓,只要每次的方向一致就行。
每次写搜索题,都因为一点细节耽误半天,555
using namespace std;
const int N=11, qsize=1000010;
int gx[] = {1,-1,0,0};
int gy[] = {0,0,1,-1};
int n, m, d, dfn, ans;
int ma[N][N], xx[N], yy[N], w[N];
int f[N][N][1<<N], v[N][N][1<<N];
char s[N<<1];
bool inq[N][N][1<<N];
struct Statu{
int x, y, z;
}q[qsize];
int main(){
scanf("%d%d%d",&n,&m,&d);
for(int i=0; i<d; ++i) scanf("%d",&w[i]);
for(int i=1, j; i<=n; ++i){
scanf("%s",s+1);
// 记录地图,ma[i][j]=0 表示可以走 (i,j),为 -1 表示不能走
for(j=1; j<=m; ++j){
if(s[j]=='0') ma[i][j]=0;
else{
if(s[j]!='#'){ xx[s[j]-'1']=i; yy[s[j]-'1']=j; }
ma[i][j]=-1;
}
}
}
int l, r, x, y, delta;
Statu cur, next, tt;
for(int i=1, j, k, b; i<=n; ++i){
for(j=1; j<=m; ++j) if(~ma[i][j]){ // 枚举起点 (i,j)
v[i][j][0] = ++dfn; f[i][j][0]=0; // v 的作用:重复利用 f[i][j][s] 数组
l = r = 0;
q[r++] = (Statu){i,j,0}; inq[i][j][0]=true;
while(l!=r){
cur = q[l];
if(cur.x==i && cur.y==j) ans = max(ans, f[cur.x][cur.y][cur.z]);
for(b=0; b<4; ++b){
x=cur.x+gx[b]; y=cur.y+gy[b];
if(x<1 || y<1 |0| x>n || y>m || ma[x][y]) continue;
delta = 0;
if(x==cur.x){ // 更新围住豆豆的状态(往哪射无所谓)
tt = cur; if(y<cur.y) tt.y=y; // 哪边开哪边闭无所谓
for(k=0; k<d; ++k) if(yy[k]==tt.y && xx[k]<tt.x){
tt.z ^= 1<<k;
if(tt.z&(1<<k)) delta+=w[k]; else delta-=w[k];
}
next = (Statu){x,y,tt.z};
}else next=(Statu){x,y,cur.z};
if(v[x][y][next.z]!=dfn || f[x][y][next.z]<f[cur.x][cur.y][cur.z]+delta-1){ // spfa
f[x][y][next.z] = f[cur.x][cur.y][cur.z]+delta-1;
v[x][y][next.z] = dfn;
if(!inq[x][y][next.z]){
inq[x][y][next.z] = true;
q[r] = next;
r = r+1>=qsize ? r+1-qsize : r+1; // 循环队列
}
}
}
inq[cur.x][cur.y][cur.z] = false;
l = l+1>=qsize ? l+1-qsize : l+1;
}
}
}
printf("%d\n",ans);
while(1); // 调试时自带
return 0;
}