蓝桥杯4(DP最短路径覆盖问题:老爷机)

1. 题目

题目描述:
这一天,无所事事的Carmen 意外地得到了一台老爷机。老爷机上有许多游戏,Carmen可以随意玩耍,但是每一个游戏都有固定的开始和结束时间。Carmen不想做一个中途挂机的坏孩子,所以每一个游戏一旦开始玩就必须玩到结束,不能中途停止去玩其他游戏,也不能从中途开始玩某一个游戏哦。

这些游戏的开始时间可能会彼此重复,所以Carmen可以在其中任意选择,但是如果Carmen在某一个时刻只有一个游戏可以玩,那么他一定会去玩。

我们假设这台老爷机上每天游戏的时间分布是固定的,而Carmen每天可以在两种游戏策略里面任意选择

第一种是Carmen在限定时间内游玩最长时间的游戏

第二种是Carmen在限定时间内游玩最短时间的游戏

因为这台机器实在是太老太破旧了,以至于Carmen如果当天选择了第一种游玩策略那么第二天他将只能选择第二种游玩策略

规定一个星期有7天

可怜的老爷机啊,请你宣判他一周只能休息多久呢。

(贪玩的Carmen会抓住一切玩的机会哦)

数据约定:
对100%的数据有1=<n<=1e5,1<=k<=1e5,保证所有输入数据合法即1<=l<r<=n+1

输入格式:
第一行一个整数n表示每天的游玩时间,一个整数k,表示这台老爷机上共有k款游戏

接下来k行,一行两个整数l和r分别表示当前这款游戏的开始和结束时间

输出格式:
一个整数代表Carmen一个星期内所能游玩的最长时间

样例输入:
15 6
1 3
1 7
4 15
8 13
8 9
11 16
样例输出:
20
提示:
在这里插入图片描述

样例解释:如图可以做出时间分布图。在1时刻我们可以选择f或者g。

如果选择f,那么到4时刻的时候会一定选择h,直至终点都无法再玩其他游戏,总游戏时间为2+11=13

如果选择g,那么到8时刻的时候可以选择i或j

如果选择i,那么到11时刻的时候一定会选择k,直至终点都无法再玩其他游戏总游戏时间为6+1+5=12

如果选择j,直至终点都无法再玩其他游戏,那么总游戏时间为6+5=11

以上我们可以得到样例中每天的最长游戏时间为13,最短游戏时间为11。那么显然老爷机只能休息(15-13)×4+(15-11)×3=20

2. 解析

1) 了解什么叫最短线段覆盖

在这里插入图片描述

如图所示,在无限延伸的坐标轴上,设定起点s以及终点e,给出一些线段(上述六种橘色),然后问最短线段覆盖是什么?
答:就是图中所圈起来的线段,不重合,使用线段最多
拓展为:一段时间内,完成的工作最多

2)解析该题

  1. 但是该题有一些拓展
    首先看到这题可能会想到贪心,但是其实贪心并不可以废话为什么呢,因为每次空闲的时候有游戏开始的话,必须选择一个游戏玩,这样就导致此刻的选择具有后效性,这样的话贪心就不适用了。所以我们要用DP,但是这里要注意的是要逆推而不能正推,因为之前的选择会对之后产生影响,但是之后并不会对之前产生影响。
  2. 题目要求老爷机的空闲时间,那我们就设dp[i]为从 i~n+1 的空闲时间,如果此刻空闲,转移方程为 dp[i]=dp[i+1]+1。如果此刻有游戏开始,转移方程dp1[i]=max(dp1[i],dp1[a[k].r])与dp2[i]=min(dp2[i],dp2[a[k].r]);
    最后输出 4dp2[1]+3dp1[1]。

3.完整AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstdlib>

using namespace std;
typedef long long ll;
struct node
{
    int l;
    int r;
};
node in[100005];
int cnt[100005];
int dp1[100005];
int dp2[100005];
bool comp(node m, node n)
{
    if (m.l != n.l)
    {
        return m.l > n.l;
    }
    else
    {
        return m.r > n.r;
    }
}
int main()
{
    memset(dp1, 0, sizeof(dp1));
    memset(dp2, 0, sizeof(dp2));
    int t, n;
    cin >> t >> n;
    dp1[0] = 0;
    dp2[0] = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> in[i].l >> in[i].r;
        cnt[in[i].l]++;
    }
    sort(in, in + n, comp);
    int p = 0;
    for (int i = t; i >= 1; i--)
    {
        if (cnt[i] == 0)
        {
            dp1[i] = dp1[i + 1] + 1;
            dp2[i] = dp2[i + 1] + 1;
        }
        else
        {
            dp1[i] = dp1[in[p].r];
            dp2[i] = dp2[in[p++].r];
            for (int j = 1; j < cnt[i]; j++)
            {
                dp1[i] = max(dp1[i], dp1[in[p].r]);
                dp2[i] = min(dp2[i], dp2[in[p++].r]);
            }
        }
    }
    cout << 4 * dp2[1] + 3 * dp1[1] << endl;
    return 0;
}

这个问题属于DP问题,动态规划问题一直是我的弱项,后序会进行重新整理,学习

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值