《思遠高考綠色通道》(Green Passage, GP)是唐山一中常用的練習冊之一,其題量之大深受lsz等許多oiers的痛恨,其中又以數學綠色通道爲最。2007年某月某日,soon-if (數學課代表),又一次宣佈收這本作業,而lsz還一點也沒有寫……
高二數學《綠色通道》總共有n道題目要寫(其實是抄),編號1..n,抄每道題所花時間不一樣,抄第i題要花a[i]分鐘。由於lsz還要準備NOIP,顯然不能成天寫綠色通道。lsz決定只用不超過t分鐘時間抄這個,因此必然有空着的題。每道題要麼不寫,要麼抄完,不能寫一半。一段連續的空題稱爲一個空題段,它的長度就是所包含的題目數。這樣應付自然會引起馬老師的憤怒。馬老師發怒的程度(簡稱發怒度)等於最長的空題段長度。
現在,lsz想知道他在這t分鐘內寫哪些題,才能夠儘量降低馬老師的發怒度。由於lsz很聰明,你只要告訴他發怒度的數值就可以了,不需輸出方案。(快樂融化:那麼lsz怎麼不自己寫程序?lsz:我還在抄別的科目的作業……)
第一行爲兩個整數n,t,代表共有n道題目,t分鐘時間。
以下一行,爲n個整數,依次爲a[1], a[2],... a[n],意義如上所述。
僅一行,一個整數w,爲最低的發怒度。
17 11
6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
3
60%數據 n<=2000
100%數據 0<n<=50000,0<a[i]<=3000,0<t<=100000000
這道題目目前只有codevs tyvj 以及SJTU ACM自己的題庫裏有···題目來自TSOI2007模擬賽 [2007-09-08]
這道題目的出題人(或者補充數據者)簡直是良心出題人阿有木有!!!五十個測試點!!!卡標算!!!
由於補充的十幾個點會卡掉n²的算法,需要用單調隊列優化···
我們首先用二分查找,對於每個mid檢查can()函數是否合法,即是否有一種方案滿足最長的空題段長度爲mid
如何檢查呢?
用f[i]表示在做第i道題的情況下,前i道題在合法狀況下(即空題段長度不超過mid)可能的所有方案中的最小時間(注意,空題段可能小於mid,這也是合法的)
那麼轉移方程爲:f[i]=min(f[j])+a[i];
其中j的範圍是i-mid-1<=j<=i-1 這是因爲我們保證a[i]入選,且由於最大空時段爲mid,枚舉j應當從i之前不做mid個的前面那一道題開始
由於每個f[j]內的狀態必然是合法的最小值,也就是最長空時段不超過mid的最小值,而j又一定被選中(做這道題),最終方案也一定是合法的。
如果還不理解,那就紙筆模擬一下吧,樣例是很好的。
單調隊列由於之前沒有相關的博文,算是這個博客中第一次出現,在這裏解釋下,基本上引用的是一位江蘇省隊神犇的論文,然而並不知道名字···
例題:一個含有n項的數列(n<=2000000),求出每一項前面的第m個數到它這個區間內的最小值。
直接求解的複雜度是O(nm)
我們維護這樣一個隊列:隊列中的每個元素有兩個域{position,value},分別代表他在原隊列中的位置和,我們隨時保持這個隊列中的元素兩個域都單調遞增。
計算時,只要在隊首不斷刪除,直到隊首的position大於等於i-m+1,那此時隊首的value必定是f[i],因爲隊列是單調的!
將a[i]插入到隊列:首先,要保證position單調遞增,由於我們動態規劃的過程總是由小到大(反之亦然),所以肯定在隊尾插入。
又因爲要保證隊列的value單調遞增,所以將隊尾元素不斷刪除,直到隊尾元素小於a[i](由於要求最小值,大的自然用不到)。
每個元素最多值入隊出隊1次,複雜度O(n)
那麼放代碼
//codevs3342 綠色通道 二分+單調隊列優化DP
//copyright by ametake
//thanks to 量子糾纏 from NUST and wu_yihao from CSDN
#include
#include
#include
using namespace std;
const int maxn=50000+10;
int n,t;
int a[maxn],f[maxn],q[maxn];
bool can(int x)
{
memset(f,0x3f,sizeof(f));
f[0]=0;
int l=0,r=0;
q[r++]=0;
for (int i=1;i<=n;i++)
{
while (l<=r&&q[l]=f[i]) r--;//刪除隊列尾部所有耗時比當前元素大的元素
q[++r]=i;
/*
//樸素的寫法是這樣的:
for (int j=max(i-x-1,0);j>1;
if (can(mid))
{
r=mid;
}
else l=mid+1;
}
printf("%d",l);
while (1);
return 0;
}