[POJ3245] Sequence Partitioning && dp单调队列+二分

想搞这道题首先你得去把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);
	}
}  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值