题解 P4772 【灰化肥,会挥发】
P4772 灰化肥,会挥发
题目大意:
给出一矩阵,求经过所有仓库的最短路。
solution:
看到很小的 \(n\) 的范围,我们考虑状压。跟 P4802 [CCO 2015]路短最 不同的是,本题给出了矩阵,所以我们考虑如何建图:
我们从每个点出发,进行 \(\text{BFS}\) ,这样就可以愉快的状压了。
设状态 \(f[\,i\,][\,j\,]\) 为当前状态为 \(\,i\,\) 走到了 \(\,j\,\) 点的最短路径长,\(g[\,i\,][\,j\,]\) 为当前状态为 \(\,i\,\) 走到了 \(\,j\,\) 点的最小字典序路径。
至于路径长,易得状态转移方程为:
\[f[\,i\,][\,j\,]= \text{max}(f[\,i\,][\,j\,],f[\,i\, \bigoplus\,(1\ll j)][\,k\,]+dis[\,k\,][\,j\,])
\]
思考一下最小字典序路径,这里 \(g[\,i\,][\,j\,]\) 我用的是 \(\text{string}\) 类型,方便字符串的拼接与比较。转移我们考虑两种情况:
- 当前最短路更新了,相应的路径也要更新。
- 当前最短路无法更新,但与原状态相等,并且当前的路径字典序较小,可更新。
细节处理:
- 只能从 \(\text{A}\) 点出发。
- 最后统计答案要赋个极大值。
看到这的同学,可以自己去写代码了(tf口吻)
代码
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int R=505,N=17;
int r,c,n;
char mp[R][R];
struct D{int x,y;}d[N];
inline D make_D(int i,int j){
D now; now.x=i,now.y=j;
return now;
}
int l[R][R]; bool vis[R][R];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
inline bool intu(int x,int y){
if(x<1||x>r||y<1||y>c) return 0;
return 1;
}
inline void bfs(int s){
memset(l,0,sizeof(l));
memset(vis,0,sizeof(vis));
vis[d[s].x][d[s].y]=1;
queue<D> q; q.push(d[s]);
while(q.size()){
int x=q.front().x,y=q.front().y;
q.pop();
for(int i=0;i<4;i++){
int zx=x+dx[i],zy=y+dy[i];
if(!intu(zx,zy)||vis[zx][zy]||mp[zx][zy]=='*')
continue;
l[zx][zy]=l[x][y]+1,vis[zx][zy]=1;
q.push(make_D(zx,zy));
}
}
}
int f[1<<N][N],dis[N][N];
string g[1<<N][N];
int main(){
scanf("%d%d%d",&r,&c,&n);
for(int i=1;i<=r;i++)
scanf("%s",mp[i]+1);
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
if('A'<=mp[i][j]&&mp[i][j]<='Z')
d[mp[i][j]-'A']=make_D(i,j);
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<n;i++){
bfs(i);
for(int j=0;j<n;j++)
dis[i][j]=l[d[j].x][d[j].y];
}
memset(f,0x3f,sizeof(f));
f[1][0]=0,g[1][0]="A";
for(int i=1;i<(1<<n);i++)
for(int j=0;j<n;j++)
if(i>>j&1)
for(int k=0;k<n;k++)
if(i>>k&1&&k!=j)
if(f[i][j]>f[i^(1<<j)][k]+dis[k][j])
f[i][j]=f[i^(1<<j)][k]+dis[k][j],
g[i][j]=g[i^(1<<j)][k]+(char)(j+'A');
else if(f[i][j]==f[i^(1<<j)][k]+dis[k][j]&&g[i][j]>g[i^(1<<j)][k])
g[i][j]=g[i^(1<<j)][k]+(char)(j+'A');
int ans1=0x7fffffff;
string ans2="ZZZZZZZZZZZZZZZZZZ";
for(int i=1;i<n;i++){
if(ans1>f[(1<<n)-1][i])
ans1=f[(1<<n)-1][i],
ans2=g[(1<<n)-1][i];
else if(ans1==f[(1<<n)-1][i]&&ans2>g[(1<<n)-1][i])
ans2=g[(1<<n)-1][i];
}
printf("%d\n",ans1);
cout<<ans2;
return 0;
}

浙公网安备 33010602011771号