NOIP2013模拟联考5】军训(training)

Description

HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:

1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。

2.每个学生必须要被分到某个班上。

3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。

4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3 的情况下,分班应使得女友指数最高的那个班的女友指数最小。

请你帮HYSBZ 的教务处完成分班工作,并输出女友指数最高的班级的女友指数。

输入数据保证题目有解。

Input

第一行仅2 个正整数n, Limit,分别为学生数量和欠扁值之和的上限。

接下来n 行每行2 个正整数Hi,Gi,分别为学号为i 的学生的欠扁值和女友数。

Output

仅1 个正整数,表示满足分班的条件下女友指数最高的班级的女友指数。

Sample Input

4 6

4 3

3 5

2 2

2 4

Sample Output

8

【样例解释】

分班按照(1,2),(3,4)进行,这时班级欠扁值之和为4+2=6<=Limit,而女友指数最高的班级为(1,2),为8。容易看出该分班方案可得到最佳答案。

Data Constraint

对于20%的数据:n,Limit<=100

对于40%的数据:n<=1000

对于100%的数据:1<=n,Gi<=20000,1<=Hi,Limit<=10^7

①Topic effect (大意)

给出H的上限,要求把给出的一个序列分成几堆,这几堆必须是连续的,每堆的 maxHi 不得大于给出的上限Limit,且使每段的 Gi 的最大和最小。

②solve

看到双最值问题,很自然地想到了二分,看到限制Limit,就可以容易的二分欠扁指数,
至于二分女友数,就需要点技巧了。。。

我们可以容易想到一个dp方程,设F[i]表示到i的最小的欠扁和,则有:

Fi=min(Fj+MaxH(j+1>i)),k=j+1iGk>LimG()

至于MaxH则表示j+1~i的H的Max

左边的dp方程随便YY一下即可,可右边的那坨东西就需要用一个数据结构来维护了,可以用辣鸡的线段树。
因为那个MaxH是从右到左单调不减的,所以可以找到一个中间点,从中间点到i这一段都修改一下。f[i]就直接查询可行区间中的最优解。做完f[i]之后还要把f[i]加入线段树。
(打得太长了,又繁琐,又难调,蒟蒻表示懒得打→_→ 囧。。。)

,也可以考虑用栈等单调队列来维护,在维护时仿似上面的做法,优点是短小精悍。。

个人采用了就是用栈维护,详见看Code

③Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define INF 2147483647
using namespace std;
const int N=20005;
int F[N],H[N],G[N],last[N],a[N],Stack[N],Pre[N],n=0,Limit=0;
int sum=0;
inline int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
bool pd(int x)
{
    memset(F,127,sizeof(F));
    F[1]=0;
    fo(i,1,n)
    {
        int l=i,r=n;
        while (l<r)
        {
            int mid=(l+r)>>1;
            if (Pre[mid]-Pre[i-1]<=x) l=mid+1;
            else r=mid; 
        }
        if (Pre[r]-Pre[i-1]<=x) r++;
        int sum=0;
        l=i;
        int MAX=0;
        while (l<=r-1)
        {
            sum+=G[l];
            F[l]=min(F[l],F[i]+MAX);
            MAX=H[l];
            l=last[l];
        }
        F[r]=min(F[r],F[i]+MAX);
    }
    if (F[n+1]<=Limit) return true;
    else return false;
}

int main()
{
    read(n);read(Limit);
    H[n+1]=INF;
    fo(i,1,n)
    {
        read(H[i]);read(G[i]);
        Pre[i]=Pre[i-1]+G[i];
        sum+=G[i];
    }  
    Stack[1]=INF;
    a[1]=n+1;
    int top=1;
    fd(i,n,1)
    {
        while (H[i]>=Stack[top]) top--;
        last[i]=a[top];
        Stack[++top]=H[i];
        a[top]=i;
    }
    int l=1,r=sum;
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (pd(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值