P1182 数列分段`Section II`
传送门
思路:
求数列每段和的最大值的最小值,很明显是用二分法求解,加贪心检验。本题关键是要怎么去高效的check,可以考虑一个贪心的思路,能加的就加上,不能则新开一段,so对于二分的值 u ,我们从数列 sum 从前往后扫,如果 tot 大于了 u ,我们不加而是 tot 重新赋值并且 cnt++ ,最后只需判断 cnt 是否不小于 m 就行了。这样判断与前缀和一样是O(n)的复杂度,但是节省了空间且容易思考。
标程:
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<queue> #include<vector> #include<deque> #include<stack> #include<map> #include<set> using namespace std; #define maxn 100005 int sum[maxn],tot,n,m,l,r,mid,cnt; inline int read() { int kr=1,xs=0; char ls; ls=getchar(); while(!isdigit(ls)) { if(ls=='-') kr=-1; ls=getchar(); } while(isdigit(ls)) { xs=(xs<<1)+(xs<<3)+(ls^48); ls=getchar(); } return kr*xs; } inline int lck(int a,int b) { return a>b?a:b; } inline int check(int u) { tot=0,cnt=0; for(int i=1;i<=n;i++) { if(tot+sum[i]<=u) tot+=sum[i]; else tot=sum[i],cnt++; } return cnt>=m; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { sum[i]=read(); l=lck(sum[i],l); r+=sum[i]; } while(l<=r) { mid=l+r>>1; if(check(mid)) l=mid+1; else r=mid-1; } printf("%d\n",l); return 0; }
注意:二分时的区间取值问题,很明显,对于l的赋值应该取数列中的最大值,而r应该取数列的总和。