1. 程式人生 > >uvalive4625(dp + 二分)

uvalive4625(dp + 二分)

題目大意:
給你一串 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; }