首页 > 代码库 > UVA - 10280Old Wine Into New Bottles(完全背包+剪枝)

UVA - 10280Old Wine Into New Bottles(完全背包+剪枝)

题目: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;
}





UVA - 10280Old Wine Into New Bottles(完全背包+剪枝)