Not Escaping(哈希+dp)

Not Escaping

[Link](Problem - E - Codeforces)

题意

给你一个 n × m n\times m n×m的楼,一些梯子(只有梯子允许跨层操作)的信息例如 a , b , c , d , h a,b,c,d,h a,b,c,d,h表示从 ( a , b ) (a,b) (a,b) ( c , d ) (c,d) (c,d)会获得 h h h滴血,给你 n n n个数 a n a_n an, a i : 在 第 i 层 每 走 一 步 损 失 多 少 滴 血 a_i:在第i层每走一步损失多少滴血 ai:i,例如在第 i i i层从 x i → x j x_i\rightarrow x_j xixj会损失 ( x j − x i ) × a i (x_j-x_i)\times a_i (xjxi)×ai滴血。

问你从 ( 1 , 1 ) (1,1) (1,1)走到 ( n , m ) (n,m) (n,m)最少损失多少滴血 ( 可 以 是 负 数 ) (可以是负数) (),如果无法从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)输出NO ESCAPE

思路

首先只有起点、终点、有梯子的点有用,因为我们一定是从起点走到某个梯子往上爬并走到下一个梯子,可以将这些点映射成从1开始的点(用个 m a p map map即可)。

我们设 f [ i ] : 走 到 第 i 个 点 最 少 损 失 多 少 滴 血 f[i]:走到第i个点最少损失多少滴血 f[i]:i,发现第 i i i个点的值可以由下面的梯子到当前这个点(如果有这样的梯子的话)和当前层的其他点通过同层走减血转移过来,也就是第 i i i个点可以由前面的点推出最优解且不会被后继影响,因此考虑 d p dp dp

对于第 i i i层的某个点 x i x_i xi分类来看对于所有在它左边的点设 x j x_j xj,从 j j j i i i的代价就是 f [ j ] + ( x i − x j ) × a i → f [ j ] − a i × x j + a i × x i f[j]+(x_i-x_j)\times a_i \to f[j]-a_i\times x_j+a_i\times x_i f[j]+(xixj)×aif[j]ai×xj+ai×xi,发现对于第 i i i层点 x i x_i xi来说 a i × x i a_i\times x_i ai×xi为定值,因此我们只需要求一个 1 ∼ i − 1 1\sim i-1 1i1里的 f [ j ] − a i × x j f[j]-a_i\times x_j f[j]ai×xj的前缀最小值。

对于某个在它右边的点 x j x_j xj,从 j j j i i i的代价就是 f [ j ] + ( x j − x i ) × a i → f [ j ] + a i × x j − a i × x i f[j]+(x_j-x_i)\times a_i \to f[j]+a_i\times x_j-a_i\times x_i f[j]+(xjxi)×aif[j]+ai×xjai×xi,同理我们只需要在 i + 1 ∼ 总 长 i + 1\sim 总长 i+1里求一个后缀最小值。

最后到了第 n n n层枚举每个点向终点跑的代价即可。

对于每个用到的没有出现过的映射一个 i d x idx idx, 对于每一行出现的点用一个 v e c t o r vector vector存对应的列和映射索引值,对于每个梯子来说用 v e c t o r vector vector来存仿照邻接表,用起点 i d x idx idx表示地址,存终点的 i d x idx idx和这个梯子的权重。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k, q;
map<PII, int> id;
vector<PII> r[N];
int a[N], cnt;
LL f[N];
vector<PII> w[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n >> m >> q;
        for (int i = 1; i <= n; i ++) cin >> a[i], r[i].clear();
        cnt = 0;
        id.clear();

        while (q --) {
            int a, b, c, d, h; 
            cin >> a >> b >> c >> d >> h;
            PII sta = {a, b}, ed ={c, d};
            if (!id[sta]) 
                id[sta] = ++cnt, r[a].push_back({b, cnt});
            if (!id[ed])
                 id[ed] = ++cnt, r[c].push_back({d, cnt});
            w[id[sta]].push_back({id[ed], h});
        }
    
        for (int i = 1; i <= cnt; i ++)
            f[i] = 1ll << 60;
        for (auto i : r[1]) 
            f[i.y] = 1ll * (i.x - 1) * a[1];
        
        for (int i = 1; i <= n; i ++) 
            if (r[i].size()) {
               int m = r[i].size(); 
               sort(r[i].begin(), r[i].end());
               LL M = 1ll << 60;
               for (int j = 0; j < m; j ++) {
                   M = min(M, f[r[i][j].y] - 1ll * r[i][j].x * a[i]);
                   f[r[i][j].y] = min(f[r[i][j].y], M + 1ll * r[i][j].x * a[i]);
               }
               M = 1ll << 61; // 多一位,因为下面的转移式位+权重,60的话会导致有的值统计不到
               for (int j = m - 1; j >= 0; j --) {
                   M = min(M, f[r[i][j].y] + 1ll * r[i][j].x * a[i]);
                   f[r[i][j].y] = min(f[r[i][j].y], M - 1ll * r[i][j].x * a[i]);
               }
               for (int j = 0; j < m; j ++)
                    if (f[r[i][j].y] < 1ll << 60ll)
                        for (auto k : w[r[i][j].y])
                            f[k.x] = min(f[k.x], f[r[i][j].y] - k.y);
            }
        LL res = 1ll << 60;
        for (auto i : r[n])
            res = min(res, f[i.y] + 1ll * (m - i.x) * a[n]);
        if (res < 1ll << 60) cout << res << endl;
        else cout << "NO ESCAPE" << endl;
        for (int i = 1; i <= cnt; i ++) 
            w[i].clear();
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值