【DP|01背包】AOJ-524 桂园食堂

69 篇文章 0 订阅
13 篇文章 0 订阅
桂园食堂
Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MB
Total Submission: 702   Submission Accepted: 110
Description
食堂对大家来说一点都不陌生,每次打菜的时候我们几乎看重两样:价钱和味道.现在你来到了桂园二楼打菜,假设你的卡里的钱为m,现在食堂里有n种菜,每种菜的价格分别为Pi,用Li来衡量你心中的每种菜的味道,我们称之为满意值.那你本次打菜最满意的值是多少呢?假设你的饭量足够大,并且你不会打两份一样的菜.

Input
有多组测试数据,对于每组数据
第一行:n m为一个整数和一个一位小数(即小数点后只保留一位)分别代表菜的种类和你的卡里的钱(0<=n<=100,0<=m<=1000)
接下来有n行,每行代表两个一位小数Pi和Li(0<=Pi<=10,0<=Li<=10)
输入到文件结束

Output
每组数据输出一个一位小数,代表最大满意值

Sample Input
Original Transformed
2 10.0
1.5 9.9
3.5 8.0
3 2.0
1.5 9.0
2.0 8.8
2.5 0.0

Sample Output
Original Transformed
17.9
9.0
———————动态—————————————的—————分割————————线———————
思路:第一次见这道题的时候,我以为每次都取性价比最高的。但是这里面是不存在性价比的。一道菜,N元钱,要么买,要么不买。买了就要付全部价钱,不买就不付钱。很明显,这是决策问题。决策牵扯到每次做了选择之后的状态。
它是01背包问题+精度。看了背包九讲第一篇,天书!
最重要的是理解这个循环
for i=1..N
   for v=V..0
        f[v]=max{f[v],f[v-c[i]]+w[i]};
它是状态转移方程的压缩一维之后。(f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]})
举个最简单的例子:
M N   从右向左看:j = M → cost[ i ]
10 3   3 4 5 6 7 8 9 10
花费 价值 i = 0 → N 价值
3 4 0 4 4 4 4 4 4 4 4
4 5 1 ———— 5 5 5 9 9 9 9
5 6 2 ———— ———— 6 6 9 10 11 11
自右向左更新f[ ]数组。在外循环第 i 个物体时f[M]正好是前 i-1 个物体放置之后的最大价值状态。每一次更新这个f[M]之前需要作出决策,放了物体 i 之后的价值,跟不放物体 i 的价值,谁大。由于是逆序,从最大花费枚举到最小花费,所以每个物体内层循环开始的时候,f[M]都是决策完前 i-1 个物体之后的最优解。
优化一下。其实我们不必开出cost数组。因为输入方式很特别!题目是同时输入花费 / 价值的。
另外,考虑精度问题。本题卡精度卡得很厉害。这个就牵扯到了浮点数精度的问题。在浮点数的表示法中,两个数之间的差值是用距离来表示的,因此相差极小的两个数,运算之后可能用同一个数来表示了。那么如何判断两个数相等呢?答案是:差值小于一个足够小的数,比如0.00000001这种。因此将cost转化成整数之前必须先使它加上0.01,避免0.99这样的浮点数误差。
代码如下:
#include <stdio.h>
int main() {
    int n, V, C;
    double VV, cc;
    while(~scanf("%d%lf", &n, &VV)) {
        int i, j;
        double f[100005] = {0}, worth[105];
        V = (int)((VV+0.01)*10);//为了处理精度问题,必须将浮点数+0.01之类很小的数字再(int)
        for(i = 0; i < n; i++) {
            scanf("%lf%lf", &cc, worth+i);
            C = (int)((cc+0.01)*10);//同理
            for(j = V; j >= C; j--)//为使DP更方便,将cost转化成整数,也就是×10
                if(f[j] < f[j-C]+worth[i])
                    f[j] = f[j-C] + worth[i];//决策
        }
        printf("%.1lf\n", f[V]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值