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; }