逃离迷宫

逃离迷宫(状压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;
}
posted @ 2017-10-31 15:52  pechpo  阅读(263)  评论(0编辑  收藏  举报