首页 > 其他 > 详细

Gym100810J Journey to the "The World's Start" (二分答案+动态规划)

时间:2020-08-01 12:10:34      阅读:88      评论:0      收藏:0      [点我收藏+]
  • 题目链接

  • 题目大意:现有n个车站,从1号车站出发,目的地为n号车站,相邻两个车站间路程消耗时间为1。但乘车需要乘车卡(一张乘车卡可以无限用但有最大连续车站限制),从中间车站下车再上车需要等待时间。现给出n-1张乘车卡的价格(第i张卡可以连续坐i站,之后需要下车再上车)和中间n-2个站点下车需要等待的时间。问能否在总时间不超过t的限制下找出最少花费,其中t >= n-1,即保证有答案。

  • 思路:首先我们知道,如果有两张可乘坐长度为a和b的卡(a < b),那么卡b是可以当做卡a使用的,即如果卡i能满足t时间限制,那么i至n-1的所有卡都能满足t时间限制,我们需要找到最小的i,取区间i到n-1中最便宜的卡即可,因此可以使用二分答案处理。 二分答案需要判断在给定最长连续乘坐长度len的情况下,能否满足时间t的限制。我们用dp[i]表示到达第i个车站需要消耗的最小等待时间,如果所有车站的dp值都小于等于t - (n - 1),说明符合要求。同时得到状态转移方程dp[i] = min(dp[j] + d[j]),表示从第j站下车后再上车到达第i站,d[j]为车站等待时间,i - j <= len。 由于时间限制,需要使用单调队列或者线段树维护指定区间dp[j]+d[j]的最小值。

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 5e4 + 10;
int p[N], d[N], n;
ll dp[N], t;

bool solve(int len) {
    memset(dp, 0, sizeof(dp));
    deque < pair<ll, int> >q;
    q.push_back(make_pair(0, 1));
    for (int i = 2; i <= n; i++) {
        //单调队列清除不符合区间要求的元素
        while (!q.empty() && q.front().second + len < i) q.pop_front();
        dp[i] = q.front().first;
        if (dp[i] > t - (n - 1)) return false;
        if (i == n) break;
        //清除比要插入元素优先级小的元素以维护单调队列
        while (!q.empty() && q.back().first >= dp[i] + d[i]) q.pop_back();
        q.push_back(make_pair(dp[i] + d[i], i));
    }
    return true;
}

int main(int argc, char const *argv[]) {
    ios::sync_with_stdio(false);
    freopen("journey.in", "r", stdin);
    freopen("journey.out", "w", stdout);
    while (cin >> n >> t) {
        for (int i = 1; i < n; i++) cin >> p[i];
        for (int i = 2; i < n; i++) cin >> d[i];
        for (int i = n - 2; i; i--) p[i] = min(p[i], p[i+1]);//令p[i]为后缀区间中的最小值
        int res = 0, l = 1, r = n - 1;
        while (r >= l) {
            int mid = (l + r) >> 1;
            if (solve(mid) == true) {
                res = p[mid];
                r = mid - 1;
            } else l = mid + 1;
        }
        cout << res << endl;
    }

    return 0;
}

Gym100810J Journey to the "The World's Start" (二分答案+动态规划)

原文:https://www.cnblogs.com/sdutzxr/p/13413401.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!