bzoj 1556 墓地秘密 —— 状压DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1556
预处理出两个障碍四个方向之间的距离(转弯次数),就可以状压DP了;
但预处理很麻烦...参考了TJ...:https://blog.csdn.net/senyelicone/article/details/56668048
用 spfa ,记录当前位置带一个朝向,然后转移时判断一下如果朝向不同就+1;
最后再从起点出发同样预处理一下,作为初始状态即可;
注意读入的地图上的 '#' 不仅是机关石,还有墙...所以不能忽略。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const maxn=105,maxm=20,inf=0x3f3f3f3f; int n,m,T,f[1<<16][maxm][5],dis[maxm][5][maxm][5],d[maxn][maxn][5]; int dx[5]={0,1,0,-1},dy[5]={1,0,-1,0},xx[maxm],yy[maxm],sx,sy,ans; bool vis[maxn][maxn],roc[maxn][maxn]; queue<pair<int,int> >q; bool ck(int x,int y){return x>0&&y>0&&x<=n&&y<=m;} void spfa(int nw,int x,int y,int dr) { if(roc[x][y]||!ck(x,y))return; while(q.size())q.pop(); memset(d,0x3f,sizeof d); q.push(make_pair(x,y)); vis[x][y]=1; for(int k=0;k<4;k++)d[x][y][k]=0; while(q.size()) { int nx=q.front().first,ny=q.front().second; q.pop(); vis[nx][ny]=0; for(int i=0;i<4;i++) { int tx=nx+dx[i],ty=ny+dy[i]; if(roc[tx][ty]||!ck(tx,ty))continue;// for(int j=0;j<4;j++) if(d[tx][ty][j]>d[nx][ny][i]+(i!=j)) { d[tx][ty][j]=d[nx][ny][i]+(i!=j); if(!vis[tx][ty])vis[tx][ty]=1,q.push(make_pair(tx,ty)); } } } for(int i=1;i<=T;i++) for(int j=0;j<4;j++)//从j撞击xi,yi { int tx=xx[i]+dx[j],ty=yy[i]+dy[j],tmp=inf; for(int k=0;k<4;k++)tmp=min(tmp,d[tx][ty][k]+(tx+dx[k]!=xx[i]||ty+dy[k]!=yy[i]));//反向 dis[nw][dr][i][j]=tmp; } } int main() { scanf("%d%d%d",&n,&m,&T); char ch[maxn]; for(int i=1;i<=n;i++) { cin>>ch; for(int j=0;j<m;j++) if(ch[j]=='#')roc[i][j+1]=1; } for(int i=1;i<=T;i++) { scanf("%d%d",&xx[i],&yy[i]); // roc[x][y]=1;//'#'表示墙,不一定是机关石!!! } for(int i=1;i<=T;i++) for(int j=0;j<4;j++) spfa(i,xx[i]+dx[j],yy[i]+dy[j],j); scanf("%d%d",&sx,&sy); spfa(T+1,sx,sy,4); memset(f,0x3f,sizeof f); f[0][T+1][4]=0; int mx=(1<<T); ans=inf; for(int s=0;s<mx;s++) for(int i=1;i<=T+1;i++) for(int j=0;j<=4;j++) if(f[s][i][j]!=inf) for(int k=1;k<=T;k++) for(int l=0;l<4;l++) f[s|(1<<(k-1))][k][l]=min(f[s|(1<<(k-1))][k][l],f[s][i][j]+dis[i][j][k][l]+1); for(int i=1;i<=T;i++) for(int j=0;j<4;j++) ans=min(ans,f[mx-1][i][j]); printf("%d\n",ans); return 0; }