UVA - 1443 Garlands(二分+DP)

题意:给你n(n<=40000)个花环,用线串起来,你需要将它挂在天花板上挂m(m<=10000)个位置(如图所示)

每个花环有一定的重量a[i]。也就是说你要把这n个花环分成m-1段。

要满足三个条件:

1、每一段都必须包含偶数个花环。

2、每半段最多d个花环。

3、使花环重量最重的半段重量最小。

注意:最后一段的最后一个点一定要挂在第m个点上(The garland has to be attached to the ceiling in m spots, where the very beginning of the garland should be attached to spot 1 and its end to spot m.)

若无论如何都不满足前两个条件,则输出BAD。

思路:二分+DP。

首先我们来看BAD的情况:

不满足第一个条件则是 n为奇数,不满足第二个条件即为n>2*(m-1)*d,还需要满足上面注意的情况,即n<2*(m-1)时不成立。

要满足第三个条件显然要二分最重的半段的重量。然后就是dp了。不难想到,设dp[i]为前i个花环能挂的最小段数,则

dp[i]=min(dp[i],dp[j]+1)     其中j+1~i这段区间的花环要满足所给条件。

最终dp[n]是否大于m-1即可判断是否符合条件。。。

然而这样却是样例都过不了。。。问题出在哪里呢?

问题就出在这组数据 

6 3 10

1 1 100 100 1 1

我们以上思路成立的条件是  段数增加,最重半段的重量减少 

但是以上数据 分成一段答案是102,分成两段答案是200

因此dp方程需要修改。

具体怎么修改我没有想到。查了一下题解才反应过来,只需要分段数的奇偶性讨论就可以了。

新的dp方程:(0表示偶数段,1表示奇数段)

dp[i][0]=min(dp[i][0],dp[j][1]+1);

dp[i][1]=min(dp[i][1],dp[j][0]+1);

其中j+1~i为1段,即包含偶数个花环且两个半段花环数量不能超过d。

初始化dp为inf,dp[0][0]=0;

时间复杂度为O(n^2*logn)  给了20000ms的时限需要优化一下,题解是及时break来提高效率,具体见代码

AC代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=40010;
int n,m,k,f,d;
int sum[maxn],dp[maxn][2];//前i个最少可以分多少段
int ans,cnt,tmp;
bool jud(int x)
{
    for(int i=0;i<=n;i++) dp[i][0]=dp[i][1]=inf;
    dp[0][0]=0;
    for(int i=2;i<=n;i+=2)
    {
        for(int len=1;(len<=d)&&(len<=i/2);len++)
        {
            if(sum[i]-sum[i-len]>x) break;//这里我用的continue 19000+ms刚好卡过,题解用的break才7000+ms
            if(sum[i-len]-sum[i-2*len]>x) continue;
            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;
    return 1;
}
int main()
{
    int T,cas=1;
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d%d%d",&n,&m,&d);
            sum[0]=0;
            for(int i=1;i<=n;i++)
            {
                int x;
                scanf("%d",&x);
                sum[i]=sum[i-1]+x;
            }
            if(n&1||n<2*(m-1)||n>2*(m-1)*d)
            {
                puts("BAD");
                continue;
            }
            int l=1,r=sum[n];
            while(l<r)
            {
                int mid=(l+r)>>1;
                if(jud(mid)) r=mid;
                else l=mid+1;
            }
            printf("%d\n",l);
        }
    }
    return 0;
}




猜你喜欢

转载自blog.csdn.net/lsd20164388/article/details/80619699