首页 > 技术文章 > 绿色通道题解

vasairg 2020-01-15 10:17 原文

绿色通道

  • 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;
}

推荐阅读