题目:Circling Round Treasures:CodeForces - 375C
题意:
给你一个N*M的地图(N,M<=20),地图上’#'表示障碍物,'B’表示炸弹,数字表示宝藏(炸弹个数+宝藏个数<=8),每个宝藏有价值(-200<=v<=200),'S’表示出发点。每次移动可以从一个格子移动到相邻格子。寻找一条路径从’S’出发再回到’S’的路径,移动步数记为K,这个路径所包围的宝藏价值总和记为V,则这条路径的价值为V-K。题目即是求可行的最大的路径价值,并且不能包围炸弹。
思路:
这里一定要注意到宝藏和炸弹个数和小于等于8个。这样就可以用状压+BFS了。用F[X][Y][S]记录最优状态,S是
2
8
2^8
28的状压,表示8个宝藏(或炸弹)目前是否被包含在路径中。F[X][Y][S]表示位于(X,Y)位置时,8个宝藏(或炸弹)包含状态为S时的路径最大价值。
一个宝藏(或炸弹)是否被包含,只要向右做一个射线,如果碰到奇数个路径点那就是包含,否则偶数为不包含。所以经过一个点时,同行左侧的宝藏(或炸弹)包含情况会被反转,重点都说完了,后面自己思考吧。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int nex[4]={1,-1,0,0},ney[4]={0,0,-1,1},q_size=410*(1<<8);
int n,m,sx,sy,ans,cnt,va[10],vx[10],vy[10],qx[q_size],qy[q_size],ql[q_size],qs[q_size],b[23][23][1<<8];
char mp[23][23];
inline int get_si(int x,int y){
int ans=0;
for (int i=1;i<=cnt;++i) if (vx[i]==x&&vy[i]>y) ans|=1<<i-1;
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%s",mp[i]+1);
for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (mp[i][j]>='0'&&mp[i][j]<='9') ++cnt,vx[mp[i][j]-48]=i,vy[mp[i][j]-48]=j;
for (int i=1;i<=cnt;++i) scanf("%d",&va[i]);
for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (mp[i][j]=='B') va[++cnt]=-1e8,vx[cnt]=i,vy[cnt]=j;else if (mp[i][j]=='S') mp[i][j]='.',sx=i,sy=j;
int h=0,t=1;
qx[1]=sx;qy[1]=sy;
while (++h<=t){
if (qx[h]==sx&&qy[h]==sy){
int sum=0;
for (int i=1;i<=cnt;++i) if ((1<<i-1)&qs[h]) sum+=va[i];
ans=max(ans,sum-ql[h]);
}
for (int i=0;i<4;++i){
int xx=qx[h]+nex[i],yy=qy[h]+ney[i],no_s=xx!=qx[h]?qs[h]^get_si(min(xx,qx[h]),yy):qs[h];
if (mp[xx][yy]=='.'&&!b[xx][yy][no_s]) b[xx][yy][no_s]=1,qx[++t]=xx,qy[t]=yy,ql[t]=ql[h]+1,qs[t]=no_s;
}
}
printf("%d",ans);
return 0;
}