uva 10280 - Old Wine Into New Bottles

来自:http://www.cnblogs.com/staginner/archive/2011/12/07/2279783.html

先把剪枝放在这里,limit=min{min*min/(max-min)}。

对于一类瓶子,我们不妨设用k个瓶子去装酒,那么能够完全装下的范围就是[k*min,k*max],随着k的增大,我们发现所有相邻区间的左端点的间距是固定的,同时区间的长度又在不断增加,于是我们猜想,到某一刻时,后面的区间相互之间会有交集,这样剩下的各个区间就会覆盖掉后面所有的整数。我们设刚开始产生交集的区间为第k个,这样有k*max>=(k+1)*min,解得k>=min/(max-min),而当酒量x>=k*min的时候,就一定能全被装进去,这样就有x>=min*min/(max-min)。

最后说一说dp的过程吧,有了上面的剪枝,我们最起码能把总酒量的状态减到4500*4500,大约是2*10^7,剩下的工作就是把瓶子按不同的容积拆成max-min+1个瓶子,然后做完全背包的dp。这样我们不妨想一下,最后瓶子数乘上酒量的状态数会不会超呢?实际上由于min<=0.99max,所以即使容量是4500也实际只有不到450000个状态,远小于2*10^7,当然最大是否有可能超过450000而且最大是多少还要根据剪枝的函数来确切算一下,而且我们dp前是可以把体积重复的瓶子去掉的,由于判重的剪枝瓶子数最后能不能到比较大的水平也是个问题,所以整体的复杂度受多方因素的制约。

就剪枝min*min/(max-min)来讲,由于min*min/(max-min)<=min*min/(min/0.99-min)<450000,所以最多不会超过450000个状态。

这样总酒量大于450000的就直接剪掉了。

 

如何dp呢?

观察可知(min,max)区间的背包等价于以下max-min+1种背包

min,min+1...,max

进而可以转换为多重背包问题

贴一下我这无耻的代码吧:

#include <iostream>

#include<cstdio>

#include<cstring>

using namespace std;

int C,n,L[105],U[105],ans,dp[450000],vis[4510],v[20000];



int main()

{

    int t,cas=1;

    scanf("%d",&t);

    while(t--)

    {

        if(cas!=1)

        puts("");

        cas++;

        scanf("%d%d",&C,&n);

        C*=1000;

        int limit=0x3f3f3f3f;

        for(int i=0;i<n;i++)

        {

            scanf("%d%d",&L[i],&U[i]);

            limit=min(limit,L[i]*L[i]/(U[i]-L[i]));

        }

        if(C>=limit)

        {

            puts("0");

            continue;

        }

        memset(dp,0,sizeof(dp));

        memset(vis,0,sizeof(vis));

        int m=0;

        for(int i=0;i<n;i++)

        for(int j=L[i];j<=U[i];j++)

        if(!vis[j])

        {

            vis[j]=1;

            v[m++]=j;

        }

        for(int i=0;i<m;i++)

        for(int j=v[i];j<=C;j++)

        dp[j]=max(dp[j],dp[j-v[i]]+v[i]);

        printf("%d\n",C-dp[C]);

    }

    return 0;

}

  

 

你可能感兴趣的:(wine)