题目:UVA - 10280Old Wine Into New Bottles(完全背包+剪枝)
题目大意:现在要将旧酒装入新瓶中,每种瓶子都有最小最大的容量要求,然后给你L升酒,在给你N个瓶子,每中瓶子的规格说明也给你,每个种类的瓶子的供应是无限的,问怎样子安排这些酒才能使得剩余的酒最少。
解题思路:这题是完全背包的题目,但是一开始就被这题的数据吓到,10^9ML,然后还有100个瓶子,瓶子的最大最小容量还相差4000左右,直接去完全背包肯定超时。之后看了大神的题接,有个规律:每个种类的瓶子的最大最小的容量是连续的,这样多次用这种瓶子,会使得最小和最大的容量值越来越靠近,最终达到了重叠的部分。所以(k + 1)* min > k * max, 这样从k * min开始后面的酒就一定可以装完。 这个是一个很大的剪枝,并且这题还要考虑容量重复的情况,用vis数组记录一下。
代码:
#include <cstdio>
#include <cstring>
const int maxn = 1e8 + 5;
const int N = 105;
const int M = 4505;
int dp[maxn];
int v[N][2];
int vis[M];
int l, n;
void init () {
memset (dp, 0, sizeof (dp));
memset (vis, 0, sizeof (vis));
for (int i = 0; i < n; i++)
for (int j = v[i][0]; j <= v[i][1] && j <= l; j++)
if (!vis[j])
vis[j] = 1;
dp[0] = 1;
}
int main () {
int t;
int maxc, minc;
scanf ("%d", &t);
while (t--) {
maxc = -1;
minc = M;
scanf ("%d%d", &l, &n);
l *= 1000;
int k = -1;
for (int i = 0; i < n; i++) {
scanf ("%d%d", &v[i][0], &v[i][1]);
if (k < v[i][0] * v[i][0] / (v[i][1] - v[i][0]))
k = v[i][0] * v[i][0] / (v[i][1] - v[i][0]);
if (maxc < v[i][1])
maxc = v[i][1];
if (minc > v[i][0])
minc = v[i][0];
}
if (l >= k) {
printf ("0\n");
} else {
init ();
if (l <= maxc && vis[l]) {
printf ("0\n");
} else {
for (int i = minc; i <= maxc; i++)
for (int j = i; j <= l; j++) {
if (vis[i] && dp[j - i])
dp[j] = 1;
}
int i;
for (i = l; i >= 0; i--)
if (dp[i])
break;
printf ("%d\n", l - i);
}
}
if (t)
printf ("\n");
}
return 0;
}