[acmm week12]二分+dp+单调队列

 

1004 抄作业
     
 
Time Limit: 1sec    Memory Limit:256MB
Description

Zfree虽然平时很爱学习,但是他迫于生活所迫(比如设计cpu实验啊),这周数分作业只能抄答案了。

可是这是他第一次抄作业题目又多,于是他有些不想抄,因为他想玩会游戏再抄,于是他打算用一定的时间去抄作业(当然也可以不用完)。

而这可能抄不完,所以他会选择跳过几题不抄,为了尽量不被老师发现,他想知道他抄写的两题的题号最大间隔最小是多少?(间隔包括他第一个抄的题目前面的题目数量 和 最后一个题目后面的题目数量)

Input

第一行 T,n 表示可以用来抄题目的总时间和题目数量(T,n<10^5)

第二行 n个数字,第i个数字表示抄写第i题要用的时间数(a[i]<T)

Output

 一个数字k,表示最多隔k题抄一题可以用T时间抄完题目

Sample Input 
 
10 5
5 3 6 1 3
 
Sample Output
 
1

 

题意:n个数,选取若干个数,其中两个数之间的间隔最大为k,问k最小是多少。

 

题解:

要最大值最小,二分k,然后dp判断当前的k是否可行。
设dp[i]表示最大间隔不超过k的前提下,前i道题目中,选了第i道题,所用的最短时间。
dp[i]=min(dp[j])+time[i], i-k-1<=j < i
这个方程最坏情况是n^2的,但这种形式的方程我们可以用一个单调队列来优化。对于当前的i,队列q储存的可能的j值,满足其dp值递增,每次先判断对首的元素是否>=i-k-1,不满足则出队;用队首元素更新dp[i];若队尾的元素的dp值大于dp[i]则出队(这些元素此后都一定没有i优,不可能用于更新其他dp值),再把i放到队尾。这样总的复杂度就是O(nlogn)的。

 

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=100010;
 5 int a[N],f[N],g[N];
 6 int t,n;
 7 
 8 void init()
 9 {
10     scanf("%d%d",&t,&n);
11     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
12     a[n+1]=0;
13 }
14 
15 bool check(int k)
16 {
17     //f[i]=min(f[j])+a[i];  (i-k-1 <= j <= i-1)
18     int l=1,r=1;
19     f[0]=0;g[1]=0;
20     for(int i=1;i<=n+1;i++)
21     {
22         while(l<=r && g[l]<i-k-1) l++;
23         f[i]=f[g[l]]+a[i];
24         while(l<=r && f[g[r]]>f[i]) r--;
25         g[++r]=i;
26     }
27     return f[n+1]<=t;
28 }
29 
30 void solve()
31 {
32     int l=0,r=n,mid;
33     while(l<r)
34     {
35         mid=(l+r)/2;
36         if(check(mid)) r=mid;
37         else l=mid+1;
38     }
39     printf("%d\n",l);
40 }
41 
42 int main()
43 {
44     //freopen("a.in","r",stdin);
45     init();
46     solve();
47     return 0;
48 }

 

posted @ 2018-11-27 20:04  拦路雨偏似雪花  阅读(...)  评论(... 编辑 收藏