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

本文介绍了一个关于分班问题的算法解决思路,该问题需要在满足特定约束条件下找到最优分班方式,使得某一指标达到最优。文章详细阐述了使用二分查找结合动态规划的方法,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题解

吐槽一句,这题水法跑得比正解快(数据水)。
由于是两个最值,那么显然想到了二分答案。
怎么判断呢?
DP 大法好,设f[i]表示到了i,满足到i结尾区间的女友值的和<当前二分出的答案时最小的欠扁值和(即最后与limit比较的东西),最后判断,只要f[n] < limit那就满足。
那么方程显然:

f[i]=minf[j]+maxh[j+1]..h[i] f [ i ] = min f [ j ] + max h [ j + 1 ] . . h [ i ]

当然要满足条件: Σk<=ik=jg[k]<=mid Σ k = j k <= i g [ k ] <= m i d
接着,水法就是用单调栈维护next[i]表示i前面第一个比h[i]大的h。然后DP时直接往前面跳就好了,由于这是水法,所以不详细讲。
正解用线段树维护f[j],max(h[j+1]..h[i]),直接求答案就好了。
code:

uses math;
var
        n,limit,i,bz,top:longint;
        l,r,mid:int64;
        h,g,last,ff,sum:array[0..20000]of longint;
        z:array[0..20000,1..2]of longint;
        f:array[0..320000,1..5]of longint; //1:max(h);2:min(h);3:min(f);4:ans;5:lazy
procedure down(x:longint);
begin
        if f[x,5]=0 then
                exit;
        f[x*2,5]:=max(f[x*2,5],f[x,5]);
        f[x*2,1]:=max(f[x*2,1],f[x,5]);
        f[x*2,2]:=f[x*2,1];
        f[x*2,4]:=f[x*2,3]+f[x*2,1];
        f[x*2+1,5]:=max(f[x*2+1,5],f[x,5]);
        f[x*2+1,1]:=max(f[x*2+1,1],f[x,5]);
        f[x*2+1,2]:=f[x*2+1,1];
        f[x*2+1,4]:=f[x*2+1,3]+f[x*2+1,1];
        f[x,5]:=0;
end;
procedure did(x:longint);
begin
        f[x,1]:=max(f[x*2,1],f[x*2+1,1]);
        f[x,2]:=min(f[x*2,2],f[x*2+1,2]);
        f[x,3]:=min(f[x*2,3],f[x*2+1,3]);
        f[x,4]:=min(f[x*2,4],f[x*2+1,4]);
end;
procedure insert(v,l,r,x,y:longint);
var
        mid:longint;
begin
        if l=r then
        begin
                f[v,1]:=h[l];
                f[v,2]:=h[l];
                f[v,3]:=y;
                f[v,4]:=h[l]+y;
                exit;
        end;
        down(v);
        mid:=(l+r) div 2;
        if x<=mid then
                insert(v*2,l,mid,x,y)
        else
                insert(v*2+1,mid+1,r,x,y);
        did(v);
end;
procedure dg(v,l,r,x:longint);
var
        mid:longint;
begin
        if f[v,1]<x then
        begin
                f[v,1]:=x;
                f[v,2]:=x;
                f[v,4]:=f[v,3]+x;
                f[v,5]:=x;
                exit;
        end;
        if l=r then
                exit;
        down(v);
        mid:=(l+r) div 2;
        if f[v*2,2]<x then
                dg(v*2,l,mid,x);
        if f[v*2+1,2]<x then
                dg(v*2+1,mid+1,r,x);
        did(v);
end;
procedure change(v,l,r,x,y,z:longint);
var
        mid:longint;
begin
        if (l=x)and(r=y) then
        begin
                dg(v,l,r,z);
                exit;
        end;
        down(v);
        mid:=(l+r) div 2;
        if y<=mid then
                change(v*2,l,mid,x,y,z)
        else
                if x>mid then
                        change(v*2+1,mid+1,r,x,y,z)
                else
                begin
                        change(v*2,l,mid,x,mid,z);
                        change(v*2+1,mid+1,r,mid+1,y,z);
                end;
        did(v);
end;
procedure find(v,l,r,x,y,z:longint);
var
        mid:longint;
begin
        if (l=x)and(r=y) then
        begin
                ff[z]:=min(ff[z],f[v,4]);
                exit;
        end;
        down(v);
        mid:=(l+r) div 2;
        if y<=mid then
                find(v*2,l,mid,x,y,z)
        else
                if x>mid then
                        find(v*2+1,mid+1,r,x,y,z)
                else
                begin
                        find(v*2,l,mid,x,mid,z);
                        find(v*2+1,mid+1,r,mid+1,y,z);
                end;
end;
function pd(x:longint):boolean;
var
        i,j:longint;
begin
        fillchar(f,sizeof(f),0);
        fillchar(ff,sizeof(ff),63);
        j:=1;
        ff[0]:=0;
        for i:=1 to n do
        begin
                while sum[i]-sum[j-1]>x do
                        inc(j);
                insert(1,1,n,i,ff[i-1]);
                change(1,1,n,j,i,h[i]);
                find(1,1,n,j,i,i);
        end;
        if ff[n]<=limit then
                exit(true);
        exit(false);
end;
begin
        readln(n,limit);
        for i:=1 to n do
        begin
                readln(h[i],g[i]);
                sum[i]:=sum[i-1]+g[i];
        end;
        for i:=n downto 1 do
        begin
                while (h[i]>z[top,1])and(top>0) do
                        dec(top);
                last[i]:=z[top,2];
                inc(top);
                z[top,1]:=h[i];
                z[top,2]:=i;
        end;
        l:=0;
        r:=400000000;
        while l<r do
        begin
                mid:=(l+r) div 2;
                if pd(mid) then
                        r:=mid
                else
                        l:=mid+1;
        end;
        writeln(l);
end.

正解打得我好辛苦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值