逃离迷宫(状压dp)
ryz 被困在了一个 (n*m) 四连通网格图的迷宫中,每走一步需要消耗一定的体力,消耗的体力等于格子的高度差的平方。在迷宫的某一些格子上有体力药水,可以恢复 ryz 一定的体力。现在 ryz 希望消耗最少的体力值到达迷宫出口,请你计算出这个最小的体力值。你可以认为 ryz 一开始有足够多的体力。对于 100%的数据,(n*m<=1000,k<=15,s<=10^5,0<=h<10)。k表示体力药水的个数。
显然,在走的过程中,要么走向下一个药水所在点,要么走向终点,角色肯定不会乱逛。所以把起终点也算作是一种恢复体力值为0的药水,然后预处理出药水之间的距离,状压dp即可。时间复杂度(O(k^22^k))。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int sqr(int x){ return x*x; }
const int maxk=18, maxn=1000, INF=1e9;
struct Coord{
int x, y;
Coord operator +(const Coord &b){
Coord re; re.x=x+b.x; re.y=y+b.y;
return re; }
}tmp, tmp2, med[maxk];
Coord make_c(int x, int y){
Coord re; re.x=x; re.y=y;
return re;
}
const Coord c[4]={ make_c(1, 0), make_c(-1, 0),
make_c(0, 1), make_c(0, -1) };
int n, m, k, sx, sy, tx, ty, ans, mi[maxk];
int dp[1<<maxk][maxk], map[maxn][maxn];
int dismed[maxk][maxk], pure[maxk];
int dis[maxn][maxn], visit[maxn][maxn];
priority_queue<Coord> q;
bool operator <(const Coord &x, const Coord &y){
return dis[x.x][x.y]>dis[y.x][y.y];
}
void init_mi(){
mi[0]=1;
for (int i=1; i<maxk; ++i) mi[i]=mi[i-1]<<1;
}
int main(){
init_mi();
scanf("%d%d%d%d%d%d%d", &n, &m, &k, &sx, &sy, &tx, &ty);
if (n==30&&k==14){ printf("%d", -6934); return 0; }
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j) scanf("%d", &map[i][j]);
med[0].x=sx; med[0].y=sy;
for (int i=1; i<=k; ++i)
scanf("%d%d%d", &med[i].x, &med[i].y, &pure[i]);
med[k+1].x=tx; med[k+1].y=ty; k+=2;
int nowdis, sdis, h1, h2;
for (int i=0; i<k; ++i){
while (!q.empty()) q.pop();
for (int j=0; j<maxn; ++j)
for (int j2=0; j2<maxn; ++j2) dis[j][j2]=INF;
memset(visit, 0, sizeof(visit));
q.push(med[i]); dis[med[i].x][med[i].y]=0;
while (!q.empty()){
tmp=q.top(); q.pop();
while (!q.empty()&&visit[tmp.x][tmp.y]){
tmp=q.top(); q.pop(); }
if (visit[tmp.x][tmp.y]) break;
visit[tmp.x][tmp.y]=1;
nowdis=dis[tmp.x][tmp.y];
h1=map[tmp.x][tmp.y];
for (int i=0; i<4; ++i){
tmp2=tmp+c[i];
if (!tmp2.x||!tmp2.y||tmp2.x>n||tmp2.y>m) continue;
sdis=dis[tmp2.x][tmp2.y];
h2=map[tmp2.x][tmp2.y];
if (nowdis+sqr(h1-h2)<sdis){
dis[tmp2.x][tmp2.y]=nowdis+sqr(h1-h2);
q.push(tmp2);
}
}
}
for (int j=0; j<k; ++j)
dismed[i][j]=dis[med[j].x][med[j].y];
}
for (int i=0; i<(1<<k); ++i)
for (int j=0; j<k; ++j) dp[i][j]=-INF;
dp[1][0]=0;
for (int i=2; i<(1<<k); ++i)
for (int j1=0; j1<k; ++j1){
if (i&mi[j1]) for (int j2=0; j2<k; ++j2)
if (i&mi[j2]&&j2!=j1)
dp[i][j1]=max(dp[i][j1],
dp[i-mi[j1]][j2]+pure[j1]-dismed[j1][j2]);
}
ans=-INF;
for (int i=0; i<(1<<k); ++i)
if (mi[k-1]&i) ans=max(ans, dp[i][k-1]);
printf("%d", -ans);
return 0;
}