我们考虑状压dp,令fi,j,k表示当前位于(i,j),k集合中的点与当前边有奇数交
而k集合外的点与当前边有偶数交的最小步数
bfs一遍可以得到
最后枚举合法的集合
时间复杂度:
O
(
n
m
k
2
k
)
O(nmk2^k)
O(nmk2k)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 22
char ch[Maxn][Maxn];
struct P{
int x,y;
}p[10];
int cnt=0;
int w[10];
int f[Maxn][Maxn][256],Q[Maxn*Maxn*256];
int hd,tl;
int nx[4]={0,0,1,-1};
int ny[4]={1,-1,0,0};
inline int merge(int x,int y,int z){return x*Maxn*(1<<cnt)+y*(1<<cnt)+z;}
inline int getx(int num){return num/Maxn/(1<<cnt);}
inline int gety(int num){return num/(1<<cnt)%Maxn;}
inline int getz(int num){return num%(1<<cnt);}
inline bool Judge(int x,int y){
if(x<1||x>n||y<1||y>m)return false;
return ch[x][y]=='.';
}
int main(){
scanf("%d%d",&n,&m);
int sx,sy;
for(register int i=1;i<=n;++i)scanf("%s",ch[i]+1);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=m;++j)
if(ch[i][j]=='S'){
sx=i;sy=j;
ch[i][j]='.';
}else if(ch[i][j]>'0'&&ch[i][j]<'9'){
p[ch[i][j]-'0'-1]=(P){i,j};
cnt=max(cnt,ch[i][j]-'0');
ch[i][j]='#';
}
int pre=cnt;
for(register int i=0;i<cnt;++i)scanf("%d",&w[i]);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=m;++j)
if(ch[i][j]=='B'){
p[cnt++]=(P){i,j};
ch[i][j]='#';
}
memset(f,63,sizeof(f));
f[sx][sy][0]=0;
hd=tl=0;
Q[tl++]=merge(sx,sy,0);
while(hd<tl){
int x=getx(Q[hd]);
int y=gety(Q[hd]);
int z=getz(Q[hd]);
hd++;
for(int i=0;i<4;++i)
if(Judge(x+nx[i],y+ny[i])){
int nz=z;
if(i==0)
for(int j=0;j<cnt;++j)
if(p[j].x>x&&p[j].y==y)
nz^=(1<<j);
if(i==1)
for(int j=0;j<cnt;++j)
if(p[j].x>x&&p[j].y==y-1)
nz^=(1<<j);
if(f[x][y][z]+1<f[x+nx[i]][y+ny[i]][nz]){
f[x+nx[i]][y+ny[i]][nz]=f[x][y][z]+1;
Q[tl++]=merge(x+nx[i],y+ny[i],nz);
}
}
}
int Ans=0;
for(int i=0;i<1<<cnt;++i){
int ans=0;
bool valid=true;
for(int j=0;j<cnt;++j){
int tmp=i&(1<<j);
if(j>=pre&&tmp)valid=false;
if(j<pre&&tmp)ans+=w[j];
}
if(!valid)continue;
Ans=max(Ans,ans-f[sx][sy][i]);
}
printf("%d\n",Ans);
return 0;
}/*
7 7
.......
.1###2.
.#...#.
.#.B.#.
.3...4.
..##...
......S
100
100
100
100
*/