Outer space invaders(代码源div.1 P5)

博主在完成一道区间动态规划题目后,领悟了将区间DP问题转化为记忆化搜索的技巧。通过实例分析,介绍了如何利用记忆化搜索策略解决消灭节点问题,展示了数形结合的方法和代码实现。

随便说点

终于算是把代码源上周div.1div.1div.1的题目写完了,虽然后面两题有很严重的作弊嫌疑(看了蜗老师的代码),但也算是懂大致的原理。不得不感慨大佬们的代码居然能如此简练,以至于我抄起来那么方便。

这题通过蜗老师的代码也给我提供了一种新的写区间dpdpdp的思路——用记忆化搜索来写区间dpdpdp。在此之前我写过一篇博客讲述了从暴搜到dpdpdp的一个思路过程,不过后来仔细想来感觉那好像只适用于记忆化搜索的分析,今天做到这题算是给了我一个新的思路——是不是所有的或者一些dpdpdp能够转化成记忆化搜索的形式?

题意

nnn个节点会在a[i]∼b[i]a[i]\sim b[i]a[i]b[i]的时间范围内出现在w[i]w[i]w[i]的位置。我们一次范围为rrr的攻击可以将当时在0∼r0\sim r0r范围内的节点全部消灭并花费rrr的费用,请问最少花费多少费用可以将所有节点全部消灭?

题解

这题的分析我们采用了数形结合的方式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KgU7Rkux-1646634572693)(/Users/chenzhiyuan/Desktop/优快云 博客/Outer space invaders(代码源div.1 P5)/Outer space invaders(代码源div.1 P5) pic1.jpg)]

列出一个坐标,xxx轴是时间坐标,yyy轴是攻击距离,如果我们在某一时刻攻击某一目标,那么被攻击到的对象都会被我们画的垂线贯穿。现在如果我们要击败距离最远的这个目标,那么我们就必须要发出距离为w[x]w[x]w[x]的一个攻击,发出这个攻击的时候路径上的目标都会被消灭。在消灭这个目标后还剩下发出攻击的左右区间中还有最大值需要我们消灭。我们只要枚举最大值存在的时间的每个时间节点然后分成左右两个区间然后再进行这样的操作就行了。

最后因为一个目标消失和一个目标出现之外的时间节点没有遍历的意义我们需要进行一次离散化。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
void solve(){
    int n;
    cin >> n;
    vector<int> a(n + 10), b(n + 10), w(n + 10), v;
    for(int i = 1;i <= n;i ++){
        cin >> a[i] >> b[i] >> w[i];
        v.push_back(a[i]);
        v.push_back(b[i]);
    }
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    int m = v.size();
    for(int i = 1;i <= n;i ++){
        a[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
        b[i] = lower_bound(v.begin(), v.end(), b[i]) - v.begin() + 1;
    }
    vector<vector<int>> dp(m + 10,vector<int> (m + 10, -1));
    auto DP = [&](auto self, int l, int r) -> int {
        if(dp[l][r] != -1) return dp[l][r];
        if(l > r) return 0;
        int& ans = dp[l][r];
        ans = inf;
        int maxid = -1, maxnum = -1;
        for(int i = 1;i <= n;i ++) if(l <= a[i] && b[i] <= r) if(w[i] > maxnum) maxnum = w[i], maxid = i;
        if(maxid == -1) return ans = 0;
        for(int i = a[maxid];i <= b[maxid];i ++) ans = min(ans, maxnum +
                                                           self(self, l, i - 1) + self(self, i + 1, r));
        return ans;
    };
    cout << DP(DP, 1, m) << endl;
}
int main(){
#ifdef DEBUG//数据量小 重定向
    freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;
    cin >> _;
    while(_ --) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值