1. 程式人生 > >uva 10280 Old Wine Into New Bottles 有剪枝的完全揹包

uva 10280 Old Wine Into New Bottles 有剪枝的完全揹包

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;

}