uvalive4625(dp + 二分)
阿新 • • 發佈:2019-02-04
題目大意:
給你一串 n 個數,表示n個球,給你這n個球的重量,要你把這n個數分成 m - 1 段,每段的數字個數都是偶數,對於每一段,它的半段數字個數都不超過d,找出一種分發,使所有的這些半段的重量的最小值,並輸出這個最小值。
思路:
詳細見程式碼解釋
程式碼:
#include <iostream>
using namespace std;
#include <cstring>
#include <stdio.h>
const int INF = 0x3f3f3f3f;
const int maxn = 40004;
int w[maxn],sum[maxn];
int n,m,d;
int dp[maxn][2];
int judge(int mid) {
dp[0][0] = 0;
dp[0][1] = INF;
for(int i = 2; i <= n; i+= 2) {//i必須是偶數 題目限制
dp[i][0] = INF;
dp[i][1] = INF;
for(int len = 1; len <= d && i - 2 * len >= 0;len++) {
if(sum[i] - sum[i - len] > mid)//要直接break 否則會TLE
break;
if(sum[i - len] - sum[i - 2 * len] <= mid) {
dp[i][0] = min(dp[i][0],dp[i - 2 * len][1] + 1);//2*len是一段 那麼dp[i][0]表示i分成偶數段 就相當於前i - 2 * len個數字分成奇數段再加上2 * len這一段變成偶數段,即dp[i - 2 * len][1]+1
dp[i][1] = min(dp[i][1],dp[i - 2 * len][0] + 1);//跟上面同理
}
}
}
if(dp[n][(m - 1)%2] > m - 1)//如果分成的段數大於m - 1的話就表示說mid太小了
return 0;
return 1;
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&n,&m,&d);
sum[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d",&w[i]);
sum[i] = sum[i - 1] +w[i];
}
if(n & 1) //如果n是奇數的話
printf("BAD\n");
else if(n < 2*(m - 1))//如果n不足以分成m - 1段
printf("BAD\n");
else if(n > 2 * d * (m - 1))//如果n比最長的數字個數還要多
printf("BAD\n");
else {//二分查詢最小值
int l = 1;
int r = sum[n];
int mid;
while(l < r) {
mid = (l + r)/2;
if(judge(mid))
r = mid;
else
l = mid + 1;
}
printf("%d\n",l);
}
}
return 0;
}