【Kickstart】2018 Round G - Cave Escape

博客介绍了陷阱问题的状态DP解法。先不考虑能量,用DFS/BFS得到每个状态的最终能量值、能到达但不在状态里的陷阱集合及能否出去,然后进行DP。设f为状态可取得的最大能量,给出初始条件和状态转移方程,采用从顶向下的记忆化搜索求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解法

看到陷阱只有15个能反应过来是状态DP,但是不知道怎么个DP法……

首先,不考虑能量够不够,每个状态直接用DFS/BFS可以得到一个最终能量值E[s],这个状态下能够到但是不在状态里的陷阱集合adjTrap[s],还有这个状态能不能出去isAble[s]

这三个东西出来之后就可以DP了,设f为状态s可以取得的最大能量:

  1. f[s]初始为-1
  2. 如果isAble[s]==true,那么f[s]=E[s]
  3. 接下来遍历adjTrap[s]里的每一个陷阱i,如果当前能量E[s]能通过这个陷阱i,那么可以尝试通过,所以状态转移方程为:f[s] = max(f[s],f[s|(1<<i)])

用从顶向下的记忆化搜索过的,不知道为什么递推版本的过不了,可能跟有的状态的E是负数有关。

#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <functional>

#define MAXN 110
#define NINF -100000
#define INF 65536


using namespace std;

typedef long long lld;

int n,m,e,sx,sy,tx,ty;
int A[MAXN][MAXN], adjTrap[INF], E[INF];
bool mark[MAXN][MAXN], isAble[INF];
vector<pair<int,int>> traps;
map<pair<int,int>,int> idx;
int dx[4] = {0,0,-1,1}, dy[4] = {-1,1,0,0};
int f[INF];

bool isSafe(const int &x, const int &y) {
    return x>=0 && x<n && y>=0 && y<m;
}

void bfs(int s) {
    int l = traps.size();
    memset(mark,0,sizeof(bool)*MAXN*MAXN);
    for(int i=0;i<l;++i) {
        if((s&(1<<i))==0) {
            mark[traps[i].first][traps[i].second] = true;
        }
    }
    queue<pair<int,int>> bfs;
    bfs.push(make_pair(sx,sy));
    mark[sx][sy] = true;
    isAble[s] = false;
    E[s] = e;
    adjTrap[s] = 0;
    while(!bfs.empty()) {
        auto a = bfs.front();
        bfs.pop();
        if(a.first==tx && a.second==ty) isAble[s] = true;
        for(int d = 0;d<4;++d) {
            int nx = a.first+ dx[d],ny = a.second+ dy[d];
            if(isSafe(nx,ny) && !mark[nx][ny] && A[nx][ny]!=NINF) {
                mark[nx][ny] = true;
                bfs.push(make_pair(nx,ny));
                E[s] += A[nx][ny];
            } else if (isSafe(nx,ny) && mark[nx][ny] && A[nx][ny]<0) {
                adjTrap[s] |= (1<<idx[make_pair(nx,ny)]);
            }
        }
    }
    adjTrap[s] ^= s;
}

int solve(int s) {
    if(f[s]!=-2) return f[s];
    f[s] = -1;
    if(isAble[s]) f[s] = E[s];
    int l = traps.size();
    for(int i=0;i<l;++i) {
        if((adjTrap[s]&(1<<i))>0 && E[s]+A[traps[i].first][traps[i].second]>=0)
            f[s] = max(f[s],solve(s|(1<<i)));
    }
    return f[s];
}

int main() {
    int t;
    scanf("%d",&t);
    for(auto round=1;round<=t;++round) {
        scanf("%d%d%d%d%d%d%d",&n,&m,&e,&sx,&sy,&tx,&ty);
        sx--, sy--, tx--,ty--;
        traps.clear();
        idx.clear();
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j) {
                scanf("%d",&A[i][j]);
                if (A[i][j]<0 && A[i][j]!=NINF){
                    idx[make_pair(i,j)] = traps.size();
                    traps.emplace_back(make_pair(i,j));
                }
            }
        }
        int l = traps.size();
        int UP = (1<<l);
        for(int s = 0;s<UP;++s)
            bfs(s);
        for(int s=0;s<UP;++s) f[s] = -2;
        printf("Case #%d: %d\n",round,solve(0));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值