[四連測(二)]奶牛慢跑
題目描述
有n(n<=100000)頭奶牛在一個無窮長的小道上慢跑。每頭奶牛的起點不同,速度也不同。小道可以被分成多條跑到。奶牛隻能在屬於自己的跑道上慢跑,不允許更換跑道,也不允許改變速度。如果要慢跑t(t<=1000000000)分鐘,要保證在任何時候不會有同一跑道上的奶牛相遇,請問最少需要多少條跑道。奶牛開始在哪條跑道是可以隨意設定的。
輸入
輸入格式:第一行兩個整數n,t。
接下來的n行,每行包含兩個整數,表示奶牛的位置和速度。位置是非負整數,速度是正整數。所有的奶牛的起點都不相同,按起點遞增的順序給出。
輸出
輸出格式:
最少的跑道數。
樣例輸入
5 3 0 1 1 2 2 3 3 2 6 1
樣例輸出
3
解題思路
要想要求出正確的答案,其實就必須要想到題目中的性質,打出合適的演算法。我們不看每一頭牛當前的速度與位置,因為這樣計算將會十分複雜,我們首先就必須要求出它們在最後的時候到達了哪裡,這樣,逆序的中途即會相遇。
那麼如何求出最少需要的跑道個數呢?考試的時候呢,我是這樣想的,每一個跑道都是上升的子序列,那麼就一定滿足條件,這樣求出最小,其實再想一想,如果求出了最長不上升子序列,不就可以得出答案了嗎?最長不上升子序列中的元素任意兩兩一定會相遇的。因此,它們都需要單獨一個跑道,而其他的呢就無所謂了。
因此,這道題就是求一個最長不上升子序列,相信這種DP題目大家都會,但樸素的DP演算法時間複雜度是N方的,此題是過不了的。那麼關鍵就是要看如何進行優化了。如果大家看過一些書籍,應該對於運用lower_bound函式進行二分優化的最長不下降子序列有一定印象。確實,這道題可以說就是運用二分進行優化。
二分要滿足單調性,因此我們需要排序。
之後,將陣列中的數一一進行二分,找到合適位置而放進去,保持陣列呈不上升。
這裡估計有人會有疑問了,為什麼這樣就真能保證數組合理呢?首先,最長肯定是對的,我們可以很輕鬆判斷出來,但是否真的存在這樣的序列呢?
我們給出簡單的一些資料進行討論 如(3 , 2 , 8) ,處理完後,就變成了( 8 , 2)。很顯然,這一種數列是不存在的。
但我們再看一下,它的長度並沒有變化。最長不下降子序列的長度的確是陣列的長度2。
那麼再在後面加一些資料? (3 , 2 , 8 ,1) , 處理完後,變成了 ( 8 ,2 , 1)。這種數列更奇怪了,然而,答案卻還是這麼多。其實,將8放入隊頭,我們可以看出這可能是一個更有用的數字,因為它大,所以後面空間可能更大。而放入隊頭,雖然看起來不合理,但並不會改變答案的最優性質。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
#include<stack>
using namespace std;
#define LL long long
#define N 100005
struct node {
long long x;
int id;
}a[N];
int n,len = 1;
long long maxt , dp [N],t;
int read(){
int f = 1 , x = 0;
char s = getchar();
while (s < '0' || s > '9'){
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9'){
x = x * 10 + s - '0';
s = getchar();
}
return f * x;
}
int solve(long long x){//二分求解
int l = 1 ,r = len + 1;
while (l + 1 < r){
int mid = (l + r)/ 2;
if (dp[mid] >= x){
l = mid;
}
else
r = mid;
}
if (dp[l] < x)
return l;
else
return r;
}
int main(){
// freopen("shuju.in","r",stdin);
n = read();
t = read();
for (int i = 1;i <= n ;i ++ ){
a[i].id = i;
long long t1 = read();
long long q = read();
a[i].x = 1ll * t1 + 1ll * q * t ;//求出終點
maxt = max (a[i].x , maxt);//確定r範圍
}
dp[1] = a[1].x;
for (int i = 2 ;i <= n; i ++ ){//將陣列每一個都進行處理
if (dp[len] >= a[i].x) {
dp[++ len] = a[i].x;
continue;
}
else{
int j = solve(a[i].x);
dp[j] = a[i].x;
len = max(len,j);
}
}
printf("%d",len);
}
總結
考試的時候,確實沒有看出這道題的性質,當時卻也是想到了最長不上升子序列,但總覺得這樣的做法不是最優,至於為什麼,我也不知道。。。考試的時候,我覺得這道題就像是遞迴求出每一個最長不下降子序列,其實如果我再細細想想的話,這道題不就是求一個最長不上升子序列嗎?主要還是被第一題困擾了,打亂了後面所有題的進度。