想搞这道题首先你得去把POJ3017做了 这道题是那道题的升级版
首先我们要寻找必须分在一起的数对 寻找的方法是:
对B排序 A从后往前找 如果某个Ai > Bj 则该Ai 一定大于所有Bj (j < i) 那么区间内所有的数都必须分在一组 为了方便处理 我们将这一组Ai 和 Bj 配对 最后需要将各个区间缩点
那么问题就转换成了 将缩点和的N个点分成若干组 每组最大值的和不超过Limit, 每组和S最大值最小问题 和POJ3017十分类似 POJ3017是将S作为限制给出的 这里却成了问题的答案
但是这样的变化很容易解决 我们只要人为给定S 就可以完全转化为POJ3017了 所以我们通过二分S 检查是否有可行分组 来解决这个问题
#include<cstdio>
#include<cstring>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<iostream>
#define INF 0x3f3f3f3f
#define MAXN 50000
using namespace std;
typedef int LL;
typedef pair<int, int> pii;
multiset <int> S;
int A[MAXN+10], sum[MAXN+10], B[MAXN+10];
int Q[MAXN+10];
int d[MAXN+10];
int n, Limit;
int a[MAXN+10], b[MAXN+10], ba[MAXN+10], bb[MAXN+10];
int N;
bool cmpb(int aa, int bf)
{
return b[aa] < b[bf];
}
bool check(int K)
{
S.clear();
int F, R;
F = Q[0] = 0;
R = d[N] = -1;
int des = 1, sum = 0;
for(int i = 1; i <= N; i++)
{
sum += B[i];
while(sum > K) sum -= B[des++];
if(des > i) return false;
while(F <= R && A[Q[R]] <= A[i])
{
if(F < R) S.erase(d[Q[R-1]] + A[Q[R]]);
R--;
}
Q[++R] = i;
if(F < R) S.insert(d[Q[R-1]] + A[Q[R]]);
while(F <= R && Q[F] < des)
{
if(F < R) S.erase(d[Q[F]] + A[Q[F+1]]);
F++;
}
d[i] = d[des-1] + A[Q[F]];
if(S.size()) d[i] = min(d[i], (LL)(*(S.begin())));
}
return d[N] <= Limit;
}
int main()
{
while(scanf("%d%d", &n, &Limit) == 2)
{
for(int i = 1; i <= n; i++) { scanf("%d%d", &a[i], &b[i]); ba[i] = bb[i] = i; }
sort(bb+1, bb+1+n, cmpb);
int i, j;
// 进行点的匹配
for(j = 1, i =n; i >= 1; i--)
while(j <= n && b[bb[j]] <= a[i])
ba[bb[j++]] = i;
//分组 在对区间内的点进行合并时将所有有交集的区间全部合在一起 因为他们都必须分为一组
int L, R;
for(i = 1, j = 1; i <= n; i = L, j++)
{
A[j] = a[i]; B[j] = b[i];
for(L = i + 1, R = max(ba[i], i); L <= R; L++)
{
A[j] = max(A[j], a[L]);
B[j] += b[L];
R = max(R, ba[L]);
}
}
N = j - 1;
L = 0, R = INF;
int Ans;
while(L < R)
{
int mid = L + ((R - L) >> 1);
if(check(mid))
R = mid;
else
L = mid+1;
}
printf("%d\n", L);
}
}