题目
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。容易看出该分班方案可得到最佳答案。
分析
HYSBZ学校有点皮 QAQ
求最大值或最小值什么的,一般都用到二分
然后还要考虑一下dp:
设f[i]为分班到第i位的最小欠扁值和
f[i]=min(f[j]+max(h[j]…h[i]),f[i])且这公式显然可以推出来
再二分出分组的界线,用前缀和sum[i]表示前i个人的g[i]的和
对于欠扁值的话,也就是在区间里取最大值就行啦!!!
程序
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,limit,h[20010],g[20010],sum[20010],last[20010],f[20010],z[20010],x[20010];
bool check(int x){
memset(f,127,sizeof(f));
f[1]=0;
for (int i=1;i<=n;i++){
int l=i,r=n;
while (l<r)
if (sum[(l+r)/2]-sum[i-1]<=x) l=(l+r)/2+1;
else r=(l+r)/2;
if (sum[r]-sum[i-1]<=x) r++;
int mx=0,ans=0;
l=i;
while (l<r){
ans+=g[l];
f[l]=min(f[l],f[i]+mx);
mx=h[l];
l=last[l];
}
f[r]=min(f[r],f[i]+mx);
}
if (f[n+1]<=limit) return true;
else return false;
}
int main(){
scanf("%d%d",&n,&limit);
for (int i=1;i<=n;i++){
scanf("%d%d",&h[i],&g[i]);
sum[i]=sum[i-1]+g[i];
}
z[1]=100000000;
x[1]=n+1;
int tot=1;
for (int i=n;i>=1;i--){
while (h[i]>=z[tot]) tot--;
last[i]=x[tot];
tot++;
z[tot]=h[i];
x[tot]=i;
}
int l=1,r=sum[n];
while (l<r)
if (check((l+r)/2)) r=(l+r)/2;
else l=(l+r)/2+1;
printf("%d",l);
}