题解 P4772 【灰化肥,会挥发】
首先发现这道题的仓库数量偏小,考虑状压。
定义状态
这个转移的话只需要在枚举下一个到达的仓库
这里的
但是我们还需要考虑输出字典序最小的方案。
这个其实并不难,只需要类比地再记录一个状态
有很多人WA就是只考虑在转移
具体细节请看丑陋的代码
#include <bits/stdc++.h>
using namespace std;
struct Node {
int x;
int y;
};
const int N = 16;
const int fx[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//四个方向
int n;
int m;
int s;
int jsq;
int f[1 << N][N]; //最小的距离
string g[1 << N][N]; //走的方案
int To[N][N]; //最短路
int Arrive[501][501];//记录以某一个点开始到所有点的最短路
char S[501][501]; //存地图
Node F[N];
void BFS(Node F) { //求从F仓库开始到其他仓库的最短路是多少
memset(Arrive, 0, sizeof(Arrive));
queue<Node>q;
q.push(F);
Arrive[F.x][F.y] = 1;
while(!q.empty()) {
Node f = q.front(); q.pop();
for(int i = 0; i < 4; i++) {
int x = f.x + fx[i][0];
int y = f.y + fx[i][1];
if(x < 1 || y < 1 || x > n || y > m || S[x][y] == '*' || Arrive[x][y]) continue; //不可以走到就跳过
Arrive[x][y] = Arrive[f.x][f.y] + 1; //更新最短路
q.push((Node){x, y}); //加入队列
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &s);
for(int i = 1; i <= n; i++)
scanf("%s", S[i] + 1); //读入地图
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) //找到仓库点
if(S[i][j] >= 'A' && S[i][j] <= 'Z')
F[S[i][j] - 'A' + 1] = (Node){i, j};
for(int i = 1; i <= s; i++) { //求出最短路
BFS(F[i]);
for(int j = 1; j <= s; j++) To[i][j] = Arrive[F[j].x][F[j].y] - 1;
}
memset(f, 63, sizeof(f)); //初始化
f[1][1] = 0;
g[1][1] = "A"; //只能从A号仓库开始
for(int i = 2; i < (1 << s); i++) {
if(!(i & 1)) continue;
for(int j = 1; j <= s; j++) {
if(i & (1 << (j - 1)) == 0) continue;
for(int k = 2; k <= s; k++) {
if(i & (1 << (k - 1)) == 0 || j == k) continue;
if(f[i][k] > f[i ^ (1 << (k - 1))][j] + To[j][k]) {
f[i][k] = f[i ^ (1 << (k - 1))][j] + To[j][k];
g[i][k] = g[i ^ (1 << (k - 1))][j] + (char)(k + 'A' - 1);
} //如果可以有更小的f更新就直接更新
else if(f[i][k] == f[i ^ (1 << (k - 1))][j] + To[j][k] && g[i][k] > (g[i ^ (1 << (k - 1))][j] + (char)(k + 'A' - 1)))
g[i][k] = (g[i ^ (1 << (k - 1))][j] + (char)(k + 'A' - 1));
//如果f相等就考虑字典序会不会更新之后更小
}
}
}
int T = (1 << s) - 1;
int Min = f[T][2];
string res = g[T][2]; //不可能最后一个点到A仓库,所有从第二个仓库开始
for(int i = 3; i <= s; i++) {
if(Min > f[T][i]) {
Min = f[T][i];
res = g[T][i];
}
else if(Min == f[T][i] && res > g[T][i])
res = g[T][i];
}//寻找最优解
printf("%d\n", Min);
cout << res << endl;
return 0;
}