UVALive 4625 Garlands(二分答案 + DP)

来源:互联网 发布:天弘基金官网淘宝店 编辑:程序博客网 时间:2024/05/06 13:24

题目大意:给你一串 n 个数,表示n个球,给你这n个球的重量,要你把这n个数分成 m - 1 段,每段的数字个数都是偶数,对于每一段,它的半段数字个数都不超过d,找出一种分发,使所有的这些半段的重量的最小值,并输出这个最小值。

思路:如果单纯从DP方面考虑,那么设状态量 d[ i ][ j ] 表示前 i 个,分成 j 段的最小值,那么复杂度是 O(n*m*d)肯定爆掉。所以这道题有一个非常巧妙的解法,那就是二分答案

当确定当前二分值 x 时,对原序列进行DP,设d[ i ] 表示前分 i 个数字的半段最大值都不超过 x 的最小段数,然后 d[ i ] = min(d[ i ] ,d[ j ] +1),i、j之间满足上述的偶数、d、x这些条件 ,可以枚举半段长度 len 。但是这样之后还有一个问题,那就是对于第三组样例  1 1 100 100 1 1 ,m = 3(即分为2段),会发现,如果 x == 102 ,那么它的最小段数是 1 ,能分成 1 段,并不意味着它一定能分成 2 段。这里还有一点,那就是奇偶性,对于已经分出的一段,它的长度是偶数,我们找到它的中点,从中点向两边再分出 1 段,那么就相当于把本来的1 段分成了 3 段,增加了 2 段,如果你直接把两边那两段直接搞成长度是 2 的,那么就可以一直那么分下去,也就是说,奇数段一定能分成奇数段,偶数段一定能分成偶数段。所以,我们再进行上述DP的时候,还要在开一维,用来表示奇偶,状态转移方程为 : d[ i ][ j ] =  min( d[ k ][ ~j ] + 1 ),i、k 满足关系。

代码如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int INF = 0x0fffffff ;const int MAXN = 40004 ;int w[MAXN],sum[MAXN];int n,m,d;int dp[MAXN][2];int check(int x){    dp[0][0] = 0;    dp[0][1] = INF;    for(int i = 2 ; i <= n ; i += 2)    {        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] > x ) break;            if(sum[i - len] - sum[i - 2*len] <= x)            {                dp[i][0] = min(dp[i][0],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) return 0;    else 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)        {            puts("BAD");        }        else if(n < 2*(m-1))        {            puts("BAD");        }        else if(n > 2*d*(m-1))        {            puts("BAD");        }        else        {            int l = 1, r = sum[n];            while(l<r)            {                int m = l+r >>1;                if(check(m))                {                    r = m;                }                else l = m+1;            }            printf("%d\n",l);        }    }    return 0;}


原创粉丝点击