Uva--10280(动规,完全背包,剪枝)

本文介绍了一个经典的动态规划问题——“旧酒新瓶”问题,即如何利用不同容量范围的瓶子来储存一定数量的酒,使得尽可能少的酒无法被储存。通过巧妙的状态转移方程和剪枝策略,解决了大规模输入下的效率问题。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

2014-08-24 00:14:23

Problem C: Old Wine Into New Bottles

Wine bottles are never completely filled: a small amount of air must be left in the neck to allow for thermal expansion and contraction. If too little air is left in the bottle, the wine may expand and expel the cork; if too much air is left in the bottle, the wine may spoil. Thus each bottle has a minimum and maximum capacity.

Given a certain amount of wine and a selection of bottles of various sizes, determine which bottles to use so that each is filled to between its minimum and maximum capacity and so that as much wine as possible is bottled.

Input

The input begins with a single positive integer on a line by itself indicating the number of the cases following, each of them as described below. This line is followed by a blank line, and there is also a blank line between two consecutive inputs.

 

The first line of input contains two integers: the amount of wine to be bottled (in litres, between 0 and 1,000,000) and the number of sizes of bottles (between 1 and 100). For each size of bottle, one line of input follows giving the minimum and maximum capacity of each bottle in millilitres. The maximum capacity is not less than 325 ml and does not exceed 4500 ml. The minimum capacity is not less than 95% and not greater than 99% of the maximum capacity. You may assume that an unlimited number of each bottle is available. 

Output

For each test case, the output must follow the description below. The outputs of two consecutive cases will be separated by a blank line.

Your output should consist of a single integer: the amount of wine, in ml, that cannot be bottled.

Sample Input

2

10 2
4450 4500
725 750

10000 2
4450 4500
725 750

Sample Output

250

0

思路:DP好题,自己想了半天,状态转移方程能想出来,但没想到剪枝,借鉴下别人的经验:http://tech.ddvip.com/2014-01/1390224962208152_2.html
引用:给定c升酒,然后有n种瓶子,每个瓶子有最少装酒min毫升和最多装酒max毫升。 并且min <= 0.99 max。要求出用这些瓶子来装酒最后剩下酒最少的升数。dp,明显是背包。但是c最大100W换算为升酒有10E。果断会超时。这时候观察题目,可以发现。假设我们用K瓶去装酒。那么左右区间长度会越来越大,最后导致重合。所以当酒量达到一定上限之后,是肯定能完全装下去的。 推导一下:设重合时候是装了k瓶,这样有。k*max>=(k+1)*min,解得k>=min/(max-min),而当酒量x>=k*min的时候,就一定能全被装进去,这样就有x>=min*min/(max-min)。

先前写了个1585ms的,有点郁闷,优化了代码,用了纯C来交,这是 52ms 水过的。(最下面附最初的1585ms慢速版QAQ)
 // 52ms
1
#include <stdio.h> 2 #include <string.h> 3 4 int dp[441046],l1[101],l2[101],exi[4501]; 5 int Case,v,n; 6 7 int main(){ 8 int i,j; 9 scanf("%d",&Case); 10 while(Case--){ 11 memset(dp,0,sizeof(dp)); 12 memset(exi,0,sizeof(exi)); 13 scanf("%d %d",&v,&n); 14 v *= 1000; 15 int tem,top = v; 16 for(i = 1; i <= n; ++i){ 17 scanf("%d %d",&l1[i],&l2[i]); 18 int tem = l1[i] * l1[i] / (l2[i] - l1[i]); 19 top = tem < top ? tem : top; 20 for(j = l1[i]; j <= l2[i]; ++j) exi[j] = 1; 21 } 22 if(v > top){ 23 printf("0\n\n"); 24 continue; 25 } 26 for(i = 308; i <= 4500; ++i){ 27 if(!exi[i]) continue; 28 for(j = i; j <= v; ++j) 29 if(dp[j - i] + i > dp[j]) 30 dp[j] = dp[j - i] + i; 31 } 32 printf("%d\n",v - dp[v]); 33 if(Case) printf("\n"); 34 } 35 return 0; 36 }

 

 
 
 1 /*************************************************************************
 2     > File Name: m.cpp
 3     > Author: Nature
 4     > Mail: 564374850@qq.com
 5     > Created Time: Sat 23 Aug 2014 04:29:24 PM CST
 6 ************************************************************************/
 7 
 8 #include <cstdio>
 9 #include <cstdlib>
10 #include <cmath>
11 #include <string>
12 #include <cstring>
13 #include <iostream>
14 #include <algorithm>
15 using namespace std;
16 typedef long long ll;
17 const int INF = 1 << 30;
18 
19 int dp[1000005];
20 int l1[105];
21 int l2[105];
22 int Case;
23 int cnt;
24 int v,n;
25 
26 int main(){
27     //freopen("in","r",stdin);
28     string str;
29     scanf("%d",&Case);
30     while(Case--){
31         cnt = 0;
32         scanf("%d %d",&v,&n);
33         v *= 1000;
34         int top = v;
35         for(int i = 1; i <= n; ++i){
36             scanf("%d %d",&l1[i],&l2[i]);
37             top = min(top,l1[i] * l1[i] / (l2[i] - l1[i]));
38         }
39         if(v > top){
40             printf("0\n\n");
41             continue;
42         }
43         memset(dp,0,sizeof(dp));
44         // dp
45         int tmin = INF;
46         dp[top] = 1;
47         for(int i = 1; i <= n; ++i){
48             for(int j = top - l1[i]; j >= 0; --j){
49                 for(int k = l1[i]; k <= l2[i]; ++k){
50                     if(j + k <= top && dp[j + k]){
51                         dp[j] = 1;
52                         tmin = min(tmin,j);
53                         break;
54                     }
55                 }
56             }
57             if(dp[0])
58                 break;
59         }
60         printf("%d\n",tmin);
61         if(Case) printf("\n");
62     }
63     return 0;
64 }
 
 

 

 

转载于:https://www.cnblogs.com/naturepengchen/articles/3932093.html

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值