題目描述
尼克天天上班以前都鏈接上英特網,接收他的上司發來的郵件,這些郵件包含了尼克主管的部門當天要完成的所有任務,每一個任務由一個開始時刻與一個持續時間構成。c++
尼克的一個工做日爲 \(n\) 分鐘,從第 \(1\) 分鐘開始到第 \(n\) 分鐘結束。當尼克到達單位後他就開始幹活,公司一共有 \(k\) 個任務須要完成。若是在同一時刻有多個任務須要完成,尼克能夠任選其中的一個來作,而其他的則由他的同事完成,反之若是隻有一個任務,則該任務必需由尼克去完成,假如某些任務開始時刻尼克正在工做,則這些任務也由尼克的同事完成。若是某任務於第 \(p\) 分鐘開始,持續時間爲 \(t\) 分鐘,則該任務將在第 \((p+t−1)\) 分鐘結束。git
寫一個程序計算尼克應該如何選取任務,才能得到最大的空暇時間。spa
輸入格式
輸入數據第一行含兩個用空格隔開的整數 \(n\) 和 \(k\)。code
接下來共有 \(k\) 行,每一行有兩個用空格隔開的整數 \(p\) 和 \(t\),表示該任務從第 \(p\) 分鐘開始,持續時間爲 \(t\) 分鐘。排序
輸出格式
輸出文件僅一行,包含一個整數,表示尼克可能得到的最大空暇時間。get
輸入輸出樣例
輸入 #1
15 6
1 2
1 6
4 11
8 5
8 1
11 5
輸出 #1
4
說明/提示
數據規模與約定
對於 \(\%100\) 的數據,保證 \(1 \leq n \leq 10^4,1 \leq k \leq 10^4,1 \leq p \leq n,1 \leq p+t-1 \leq n\)it
又雙叒叕來刷\(DP\)水題難題了io
一開始我從任務排序以後來作 \(DP\), 經歷現實的毒打以後就放棄了這種\(naive\)的想法。class
因而咱們考慮直接從時間角度來遞推:程序
考慮 \(f[i]\) 表示從第 \(i\) 分鐘到末尾的最大休息時間。
對於每個任務,咱們記錄它們的起始時間,這個起始時間能夠從它的結束時間轉移而來。
而若是在某個時間沒有起始任務,就能夠先當成尼克在這個時間點進行休息。
因而乎咱們有以下 \(DP\):
\(f[i]= \left\{\begin{array}\\f[i-1]+1,start[i]==0\\f[i + t[j]],j\in start[i] \end{array}\right.\)
\(start[i]\) 表示從時間點\(i\) 開始的任務集合。但其實記錄的時候只須要記下個數就好,咱們拍好順序從後往前推即可以求得哪些工做是以\(i\) 爲起始時間。
#include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;i++) #define repd(i,a,b) for(int i=a;i>=b;i--) #define _(d) while(d(isdigit(ch=getchar()))) template <class T> void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;} const int N=1e4+4; struct A{ int l, t; bool operator<(const A rhs)const{ return l > rhs.l; } }a[N]; int n, k, f[N], s[N]; int main(){ g(n), g(k); rep(i, 1, k){ g(a[i].l), g(a[i].t); s[a[i].l]++; } sort(a+1, a+1+k); int id=1; repd(i, n, 1){ if( !s[i] ) f[i]=f[i+1] + 1; else{ rep(j, 1, s[i]){ f[ i ]=max( f[ i ], f[ i + a[id].t ] ); id ++; } } } printf("%d\n",f[1]); return 0; }