绿色通道
- Description
高二数学《绿色通道》总共有n道题目要写(其实是抄),编号1..n,抄每道题所花时间不一样,抄第i题要花a[i]分钟。由于lsz还要准备NOIP,显然不能成天写绿色通道。lsz决定只用不超过t分钟时间抄这个,因此必然有空着的题。每道题要么不写,要么抄完,不能写一半。一段连续的空题称为一个空题段,它的长度就是所包含的题目数。这样应付自然会引起马老师的愤怒。马老师发怒的程度(简称发怒度)等于最长的空题段长度。
现在,lsz想知道他在这t分钟内写哪些题,才能够尽量降低马老师的发怒度。由于lsz很聪明,你只要告诉他发怒度的数值就可以了,不需输出方案。(快乐融化:那么lsz怎么不自己写程序?lsz:我还在抄别的科目的作业……)
- Input
第一行为两个整数n,t,代表共有n道题目,t分钟时间。以下一行,为n个整数,依次为a[1],a[2],... a[n],意义如上所述。
- Output
一个整数w,为最低的发怒度。
- Sample Input 1
17 11
6 4 5 2 5 3 4 5 2 3 4 5 2 3 6 3 5
- Sample Output 1
3
- Hint
分别写第4,6,10,14题,共用时2+3+3+3=11分钟。空题段:1-3(长度为3), 5-5(1), 7-9(3), 11-13(3), 15-17(3)。所以发怒度为3。可以证明,此数据中不存在使得发怒度≤2的作法。数据规模:60%数据 n≤2000,100%数据 0<n≤50000,0<a[i]≤3000,0<t≤100000000
主要思路
- 二分答案,发怒值\(d[n+1]\)越大,抄题时间\(t\)越短,用二分枚举发怒值。
- 当\(d[n+1]<=t\)时,满足题意,\(r=mid-1\)
- 当\(d[n+1]>t\)时 ,不满足题意,\(l=mid+1\)
- \(DP\),求在该发怒值下,最短抄题时间
- 状态转移方程 \(d[i]=min\left\{d[j]\right\}(i-m-1<=j<i,j>=0)+a[i],1<=i<=n+1\)
- \(d[i]\)表示\(1\)~\(i\)题发怒值不超过\(m\)的最短抄题时间
优化
- 用单调队列或优先队列优化\(DP\)
\(code\)
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int Max=50010;
int n,t;
int a[Max];
int f[Max];
struct node
{
int pos,val;
friend bool operator <(node a,node b)
{
return a.val>b.val;
}
};
bool pd(int m)
{
memset(f,0,sizeof(f));
priority_queue<node> Q;
Q.push((node){0,0});
for(int i=1; i<=n+1; i++)
{
while(!Q.empty()&&Q.top().pos<=i-m-2) Q.pop();
f[i]=Q.top().val+a[i];
Q.push((node){i,f[i]});
}
return f[n+1]<=t;
}//优先队列优化
bool ok(int w)
{
int f[50010],q[50010];
int k,l=0,r=0;
f[0]=0,q[0]=0;
for(int i=1; i<=n+1; i++)
{
k=f[q[l]]+a[i];
while(r>=l&&k<=f[q[r]]) r--;
r++;
q[r]=i;
f[i]=k;
if(i-q[l]>w) l++;
}
return f[n+1]<=m;
}//单调队列优化
int solve()
{
int l=0,r=n,mid,ans;
while(l<=r)
{
mid=(l+r)>>1;
if(pd(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}//二分答案
int main()
{
scanf("%d%d",&n,&t);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
cout<<solve()<<endl;
return 0;
}