[DP][二分]JZOJ 3463 军训
Posted mastervan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[DP][二分]JZOJ 3463 军训相关的知识,希望对你有一定的参考价值。
分析
由于求max-min问题,发现满足二分性以后,可以考虑二分
然后我们想到DP判断
fi为到i为止(不包括i)的最小欠扁值之和
然后方程容易得到:
f[i]=∑min(f[j]+max(h[j]..h[i]))
但是枚举j显然不太现实,O(n^2)的时间复杂度
然后注意到max(f[j]..f[i])是满足单调性的(单调下降或不变)
那么我们设nexti为从i往后第一个满足hnext[i]>hi的位置
那么显然若i≤j<nexti,则max(h[i]..h[j])不变
那么我们可以直接跳过j,跳到nexti即可
时间复杂度为O(knlogn)k为常数(不会算next的时间复杂度,因为数据是可以卡掉的,如果h满足单调上升就可以卡住next,使k退化为n,但是显然没有卡【滑稽】)
#include <iostream> #include <cstdio> #include <memory.h> #define rep(i,a,b) for (i=a;i<=b;i++) const int N=20002; using namespace std; int next[N],nstk[N]; long long sumofg[N],stk[N]; int n; long long limit; int g[N],h[N]; int Get_Right(int x,long long lmt) { int l=x,r=n,mid; while (l<r) { mid=l+r>>1; if (sumofg[mid]-sumofg[x-1]>=lmt) r=mid; else l=mid+1; } return l; } bool Judge(long long lmt) { int i; long long f[N]; memset(f,0xf,sizeof f); f[1]=0; rep(i,1,n) { int j=Get_Right(i,lmt),k; long long mx; if (sumofg[j]-sumofg[i-1]>lmt) j--; k=i;mx=h[i]; while (k<=j) { f[k]=min(f[k],f[i]+mx); mx=h[k]; k=next[k]; } f[j+1]=min(f[j+1],f[i]+mx); } return f[n+1]<=limit; } int main() { int i,top=1; scanf("%d%lld",&n,&limit); rep(i,1,n) { scanf("%d%d",&h[i],&g[i]); sumofg[i]=sumofg[i-1]+g[i]; } stk[top]=2147483647; nstk[top]=n+1; for (i=n;i;i--) { while (h[i]>=stk[top]) top--; next[i]=nstk[top]; top++; stk[top]=h[i]; nstk[top]=i; } long long l=1,r=sumofg[n],mid,ans=sumofg[n]; while (l<r) { mid=l+r>>1; if (Judge(mid)) { ans=min(ans,mid); r=mid; } else l=mid+1; } printf("%lld",ans); }
以上是关于[DP][二分]JZOJ 3463 军训的主要内容,如果未能解决你的问题,请参考以下文章