题目链接:
http://poj.org/problem?id=3245
题目大意:
有n个连续的数对(Ai,Bi).求一个划分,使得每一部分的B的和的最大值最小。
划分要求:
1、对于任意的p<q , p,q属于不同的部分,则有Bp>Aq.
2、对于每一部分的最大A的和小于给定的lim.
解题思路:
当p<q是如果Bp<=Aq则p,q一定是属于一个部分。
先把必须在一起的粗略划分下,然后二分B的和的最小值。
dp[i]表示到达第i个数对时,此时的最大的A的和。
dp[i]=min{dp[j]+max(Ak,j+1<=k<=i),其中b(j+1)+...+b(i)<=二分的值。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF 0x1f1f1f1f #define PI acos(-1.0) #define ll __int64 #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 using namespace std; /* freopen("data.in","r",stdin); freopen("data.out","w",stdout); */ #define M 55000 int n,lim,a[M],b[M],p[M],q[M]; //p[i]表示将b数组从小到大排序后的下标, //q[i]表示b中的第i号元素与a中的q[i]号元素可能有冲突 int dp[M]; //dp[i]表示到达第i个数对时,a中能达到的最小值 bool cmp(int i,int j) { return b[i]<b[j]; //按b数组排序,将下标对应交换 } multiset<int>sbt; //多重集合容器,里面的存储结构是平衡二叉树 bool iscan(int m) { int p=1,r=-1,le=0; ll sum=0; sbt.clear(); for(int i=1;i<=n;i++) { sum+=b[i]; while(sum>m) sum-=b[p++]; if(p>i) return false; //说明有单个的值超过了m while(le<=r&&a[i]>=a[q[r]]) //去掉后面的小的 { if(le<r) sbt.erase(dp[q[r-1]]+a[q[r]]); //注意使得A的最大值和最小时, //可以以最大的a[i]为分界点 r--; } q[++r]=i; if(le<r) //如果队列至少有两个元素 sbt.insert(dp[q[r-1]]+a[q[r]]); while(le<=r&&q[le]<p) { if(le<r) sbt.erase(dp[q[le]]+a[q[le+1]]); le++; } dp[i]=a[q[le]]+dp[p-1]; ll tmp=*sbt.begin(); if(le<r&&tmp<dp[i]) //队列中至少有两个元素 dp[i]=tmp; } return dp[n]<=lim; } int main() { while(scanf("%d%d",&n,&lim)!=EOF) { for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); q[i]=p[i]=i; } sort(p+1,p+n+1,cmp); for(int i=n,j=1;i>=1;i--) while(j<=n&&b[p[j]]<=a[i]) //将b中与a冲突的标记 q[p[j++]]=i; //将b全部用完,尽量去消耗下标值大的, //小的不用管了,找到最大冲突区间 int le,ri,j=1; for(int i=1;i<=n;i=le,j++) //有冲突一定要放在一起 { a[j]=a[i],b[j]=b[i]; for(le=i+1,ri=max(q[i],i);le<=ri;le++) { a[j]=max(a[j],a[le]); b[j]+=b[le]; ri=max(ri,q[le]); //又产生了新的冲突 } } n=j-1;//现在一共只有j-1组了 le=1,ri=((1<<31)-1); //printf("%d\n",ri); int m,ans; while(le<=ri) { m=(le+ri)>>1; if(iscan(m)) ans=m,ri=m-1; //能否更小 else le=m+1;//不行的话,增大 } printf("%d\n",ans); } return 0; }