【DP】老爷机

Description

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

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

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

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

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

规定一个星期有7天

可怜的老爷机啊,请你宣判他一周只能休息多久呢。
(贪玩的Carmen会抓住一切玩的机会哦)

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

Input

第一行一个整数n表示每天的游玩时间,一个整数k,表示这台老爷机上共有k款游戏。
接下来k行,一行两个整数l和r分别表示当前这款游戏的开始和结束时间。

Output

一个整数代表Carmen一个星期内所能游玩的最长时间。

Sample Input:

15 6
1 3
1 7
4 15
8 13
8 9
11 16

Sample Output:

20

Hint

在这里插入图片描述

样例解释:如图可以做出时间分布图。在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

Solution

题目要求计算老爷机的休息时间(即空闲时间),根据每周7天,每天轮换游玩策略,可以得到最终空闲时间就是 4*一天最长游玩时间 + 3*一天最短游玩时间

关键问题为计算一天内游玩老爷机的最长和最短时间。
采用DP的方法计算一天内的游玩时间,尽可能使该时间分别达到最大和最小。

从后向前(i: n-1 -> 0)计算从 i 时刻开始这一天的最大最小游戏时间。
dpmax[i] 表示 从时刻 i 开始能达到的最大游玩时间;
dpmin[i] 表示 从时刻 i 开始能达到的最小游玩时间。
lnum[i] 表示当前时刻 i 开始的游戏的数量。

在任一时刻 i 有以下两种情况:
1、不存在 i 时刻开始的游戏
dp[i] = dp[i+1] (同时适用于 dpmax 和 dpmin)
2、存在 i 时刻开始的游戏,该游戏开始时间为 s,结束时间为 e,持续时间为 t
dp[i] = dp[e] + t
对于 i 时刻多个同时开始的游戏:
取能使 dpmax[e] + t 达到最大的计算新的 dpmax;
取能使 dpmin[e] + t 达到最小的计算新的 dpmin。

计算题目要求的最终答案输出: 4*(n-dpmax[0]) + 3*(n-dpmin[0])

Code

#include <iostream>
#include <algorithm>
using namespace std;

int cmp(const pair<int,int> p1,const pair<int,int> p2)
{
	if(p1.first!=p2.first)return p1.first>p2.first;
	else return p1.second>p2.second;
}

int main()
{
	int n,k;
	cin>>n>>k;
	pair<int, int> games[100000];
	int lnum[100000];
	for(int i=0;i<k;i++)
	{
		cin>>games[i].first>>games[i].second;
		lnum[games[i].first]++;
	}
	sort(games,games+k,cmp);
	int dpmin[100000]={0},dpmax[100000]={0};
	int cp = 0;
	for(int i=n-1;i>=0;i--)
	{
		if(lnum[i]!=0)
		{
			dpmin[i]=0x7FFFFF,dpmax[i]=0;
			for(int j=0;j<lnum[i];j++)
			{
				dpmin[i] = min(dpmin[games[cp+j].second] + games[cp+j].second - games[cp+j].first, dpmin[i]);
				dpmax[i] = max(dpmax[games[cp+j].second] + games[cp+j].second - games[cp+j].first, dpmax[i]);
			}
			cp += lnum[i];
		}
		else
		{
			dpmax[i] = dpmax[i+1];
			dpmin[i] = dpmin[i+1];
		}
	}
	cout<<4*(n-dpmax[0])+3*(n-dpmin[0]);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值