UVa 10280 Old Wine Into New Bottles / 完全背包

转自http://blog.csdn.net/yan_____/article/details/8671147

1、这道题如果直接把每个酒瓶的可用容量来做完全背包的话会超时,但是由最低的容量不低于95%,最高的容量不超过99%,由于容量的连续性有一些规律可循,可藉此优化:

   考虑任意一种瓶子能够将酒全装满的情况,最小容量min,最大容量max,只要酒的体积x在[min,max]|[2*min,2*max]|……[k*min,k*max]的范围内就能完全装满,而max比min大,倍乘后单个区间长度会越来越长,而区间左端保持等距,右端距离越来越远,每当增加一个k,上一区间的右端点与当前区间的左端点距离就会减小(max-min),所以当(k+1)增加到(min*2-max)/(max-min)时各个区间就会相连,即min*(k+1)>max*k,得到k<min/(max-min),我们要求的边界就是k*min<min*min/(max-min),在这之后x都能装满

举个例子min=95,max=99

95     99
190     198
285     297
380     396
475     495
570     594
665     693
760     792
855     891
950     990
1045     1089
1140     1188
1235     1287
1330     1386
1425     1485
1520     1584
1615     1683
1710     1782
1805     1881
1900     1980
1995     2079
2090     2178
2185     2277
2280     2376//这里开始相连,2280是边界
2375     2475
2470     2574
2565     2673
2660     2772
2755     2871
2850     2970
2945     3069
3040     3168
3135     3267
3230     3366
3325     3465
3420     3564

2、瓶子最多有100个,全部满足的容量可能会有重叠,如果忽视重复容量会超时

#include<stdio.h>
#include<string.h>
#define s 2500
#define maxn 450000
int v;
int dp[maxn],vis[4600],c[4600];
int a[110],b[110];
int main()
{
    int i,j,k,l,n,m;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        k=1<<30;
        scanf("%d %d",&v,&n);
        v=v*1000;
        dp[0]=1;
        for(i=0;i<n;i++)
        {
            scanf("%d %d",&a[i],&b[i]);
            if(k>a[i]*a[i]/(b[i]-a[i]))
                k=a[i]*a[i]/(b[i]-a[i]);
        }
        if(v>=k)
        {
            printf("0\n");
            if(t)
            printf("\n");
        }
        else
        {
            m=0;
            memset(vis,0,sizeof(vis));
            memset(dp,0,sizeof(dp));
            for(i=0;i<n;i++)
            {
                for(j=a[i];j<=b[i];j++)
                {
                    if(!vis[j])
                    {
                        c[m++]=j;
                        vis[j]=1;
                    }
                }
            }
            dp[0]=1;
            for(i=0;i<m;i++)
            {
                for(j=c[i];j<=v;j++)
                    if(dp[j-c[i]]==1)
                        dp[j]=1;
            }
            for(i=v;i>=0;i--)
            {
                if(dp[i])
                    break;
            }
            printf("%d\n",v-i);
            if(t)
            printf("\n");
        }
    }
    return 0;

}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值