[DP][二分]JZOJ 3463 军训

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

分析

由于求max-min问题,发现满足二分性以后,可以考虑二分

然后我们想到DP判断

fi为到i为止(不包括i)的最小欠扁值之和

然后方程容易得到:

f[i]=∑min(f[j]+max(h[j]..h[i]))

但是枚举j显然不太现实,O(n^2)的时间复杂度

然后注意到max(f[j]..f[i])是满足单调性的(单调下降或不变)

那么我们设nexti为从i往后第一个满足hnext[i]>hi的位置

那么显然若i≤j<nexti,则max(h[i]..h[j])不变

那么我们可以直接跳过j,跳到nexti即可

时间复杂度为O(knlogn)k为常数(不会算next的时间复杂度,因为数据是可以卡掉的,如果h满足单调上升就可以卡住next,使k退化为n,但是显然没有卡【滑稽】)

#include <iostream>
#include <cstdio>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=20002;
using namespace std;
int next[N],nstk[N];
long long sumofg[N],stk[N];
int n;
long long limit;
int g[N],h[N];

int Get_Right(int x,long long lmt) {
    int l=x,r=n,mid;
    while (l<r) {
        mid=l+r>>1;
        if (sumofg[mid]-sumofg[x-1]>=lmt) r=mid;
        else l=mid+1;
    }
    return l;
}

bool Judge(long long lmt) {
    int i;
    long long f[N];
    memset(f,0xf,sizeof f);
    f[1]=0;
    rep(i,1,n) {
        int j=Get_Right(i,lmt),k;
        long long mx;
        if (sumofg[j]-sumofg[i-1]>lmt) j--;
        k=i;mx=h[i];
        while (k<=j) {
            f[k]=min(f[k],f[i]+mx);
            mx=h[k];
            k=next[k];
        }
        f[j+1]=min(f[j+1],f[i]+mx);
    }
    return f[n+1]<=limit;
}

int main() {
    int i,top=1;
    scanf("%d%lld",&n,&limit);
    rep(i,1,n) {
        scanf("%d%d",&h[i],&g[i]);
        sumofg[i]=sumofg[i-1]+g[i];
    }
    stk[top]=2147483647;
    nstk[top]=n+1;
    for (i=n;i;i--) {
        while (h[i]>=stk[top]) top--;
        next[i]=nstk[top];
        top++;
        stk[top]=h[i];
        nstk[top]=i;
    }
    long long l=1,r=sumofg[n],mid,ans=sumofg[n];
    while (l<r) {
        mid=l+r>>1;
        if (Judge(mid)) {
            ans=min(ans,mid);
            r=mid;
        }
        else l=mid+1;
    }
    printf("%lld",ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/mastervan/p/9387510.html
今日推荐