[二分][dp] Jzoj P3463 军训
题解
- 一看题目,要我们求最大值最小,显然二分
- 设二分出一个t表示分班后的最大的女友指数,显然后面划分出来的所有班都要小于t
- 那么现在问题转换为求这样分班后是否可行
- 题目还有一个条件:所有班的欠扁值之和不得超过Limit
- 考虑一下dp
- 设f[i]为分班到第i位的最小欠扁值和
- f[i]=min(f[j]+max(h[j]...h[i]),f[i])且
- 这公式显然可以推出来
- 这样的话时间复杂度就是O(n log n^2),显然会炸
- 这样的话,考虑再二分出分组的界线,用前缀和sum[i]表示前i个人的g[i]的和
- 对于欠扁值的话,也就是在区间里取最大值
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int inf=10000000000; 7 int n,limit,h[20010],g[20010],sum[20010],last[20010],f[20010],z[20010],x[20010],tot,l,r,mid; 8 bool check(int x) 9 { 10 int l,r,mx,ans,mid; 11 memset(f,127,sizeof(f)); 12 f[1]=0; 13 for (int i=1;i<=n;i++) 14 { 15 l=i; r=n; 16 while (l<r) 17 { 18 mid=(l+r)/2; 19 if (sum[mid]-sum[i-1]<=x) l=mid+1; else r=mid; 20 } 21 if (sum[r]-sum[i-1]<=x) r++; 22 ans=0; l=i; mx=0; 23 while (l<r) 24 { 25 ans+=g[l]; 26 f[l]=min(f[l],f[i]+mx); 27 mx=h[l]; l=last[l]; 28 } 29 f[r]=min(f[r],f[i]+mx); 30 } 31 if (f[n+1]<=limit) return true; else return false; 32 } 33 int main() 34 { 35 scanf("%d%d",&n,&limit); 36 for (int i=1;i<=n;i++) 37 { 38 scanf("%d%d",&h[i],&g[i]); 39 sum[i]=sum[i-1]+g[i]; 40 } 41 z[1]=inf; x[1]=n+1; tot=1; 42 for (int i=n;i>=1;i--) 43 { 44 while (h[i]>=z[tot]) tot--; 45 last[i]=x[tot]; 46 tot++; 47 z[tot]=h[i]; x[tot]=i; 48 } 49 l=1,r=sum[n]; 50 while (l<r) 51 { 52 mid=(l+r)/2; 53 if (check(mid)) r=mid; else l=mid+1; 54 } 55 printf("%d",l); 56 }