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