没做出来看题解的时候就在想出题人是怎么搞出来的。。。这个做法emm
最大值最小化肯定要想二分,但是二分后这题目怎么check是个技术活。我最悲催的是理解错了题目意思,以为这些珠子(不管是什么吧我感觉是珠子)可以互换位置。实际上是不可以换的emm所以说看题还是要好好搞明白。
然后说说这个check,这个一开始有点迷,因为它本身是dp那一章的题,但是二分的check一般都是判断一个可行性,那加了这个二分出来的答案的约束怎么判断可行性?首先可以确定一般dp不会说去dp那种“区间上的最大值”之类的东西,他要做一定是一个量化的东西去求最优解。这里的话可以想象一下,如果用了比m更少的段就可以满足要求那么就可以继续进行下去?这样的话必须保证段数和最紧的上限是一个正比的关系!但是算一算可以发现并没有这种正比关系,一般这时候就放弃用二分的方法了,但是这个题最精彩的地方就在于他的奇偶性,由于题目本身一直在要求“半段”,所以可以利用奇偶性来保证正比单调,具体可以看这位dalao的博客
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e4+15;
const int INF=0x3f3f3f3f;
int wt[maxn];
int dp[maxn][2];
int psum[maxn];
int d,n,m;
bool check(int x)
{
memset(dp,INF,sizeof(dp));dp[0][0]=0;
for(int i=2;i<=n;i+=2)
for(int j=i-1;j>=1&&(i-j+1)<=2*d&&psum[i]-psum[(i+j)>>1]<=x;j-=2)
if(psum[(i+j)>>1]-psum[j-1]<=x)
dp[i][0]=min(dp[i][0],dp[j-1][1]+1),dp[i][1]=min(dp[i][1],dp[j-1][0]+1);
return ((m-1)%2)?(dp[n][1]<m):(dp[n][0]<m);
}
int main()
{
int t,L,R,M;
scanf("%d",&t);
while(t--)
{
L=R=0;
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=n;i++)
{
scanf("%d",&wt[i]);L=min(L,wt[i]),R+=wt[i];
}
if(2*d*(m-1)<n||n<2*(m-1)||n%2)
{
printf("BAD\n");continue;
}
R=((R+1)>>1);
for(int i=1;i<=n;i++)
psum[i]=psum[i-1]+wt[i];
while(L<R)
{
M=(L+R)>>1;
if(check(M))
R=M;
else
L=M+1;
}
printf("%d\n",L);
}
return 0;
}
