正题
大意
每个人有两个值gigi和hihi,要求将序列分解成连续的几个序列。要求每个序列最大的hihi的和小于Limt的情况下每个序列gigi的和的最大值最小。
解题思路
我们二分最小的gigi的和的最大值
首先一个O(n2)O(n2)的dp想法,用fifi表示分割到第i个时最大的hihi的和的最小值。
动态转移:
fi=min{f[j]+max{hj,hj+1,hj+2...hi−1,hi}}fi=min{f[j]+max{hj,hj+1,hj+2...hi−1,hi}}
我们考虑如何优化,每次有新的max只会在产生更大的hihi时,于是我们可以用一个nextinexti表示最近的hnext>hihnext>hi,然后我们可以二分快速找到满足小于你目前二分到的答案的最小的位置。
时间复杂度O(n2+n (log n)2)O(n2+n(logn)2)
是不是感觉时间复杂度没有变化,巧了!它就是A了
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define H 20010
#define ll long long
using namespace std;
int n,h[H],g[H],z[H],next[H],num[H],tot;
ll sum[H],L,f[H],ans,l,r;
int find(int x,int need)//二分满足条件的最小值
{int l=x,r=n;while(l<r){int mi=(l+r)/2;if(sum[mi]-sum[x-1]>=need) r=mi;else l=mi+1;}return l;
}
bool check(int x)
{memset(f,127/3,sizeof(f));f[1]=0;for (int i=1;i<=n;i++)//dp{int k=find(i,x),y=i,add=h[y];if (sum[k]-sum[i-1]>x) k--;while(y<=k){f[y]=min(f[y],f[i]+add);add=h[y];y=next[y];}f[k+1]=min(f[k+1],f[i]+add);}return f[n+1]<=L;
}
int main()
{scanf("%d%lld",&n,&L);for(int i=1;i<=n;i++){scanf("%d%d",&h[i],&g[i]);sum[i]=sum[i-1]+g[i];}num[1]=n+1;z[1]=2147483647;tot=1;for (int i=n;i;i--)//暴力next数组{while (z[tot]<=h[i]) tot--;next[i]=num[tot];++tot;num[tot]=i;z[tot]=h[i];} l=1;r=sum[n];ans=r;while(l<=r)//二分{int mid=(l+r)>>1;if (check(mid)) {if (mid<ans)ans=mid;r=mid-1;}else l=mid+1;}printf("%lld",ans);
}