桂园食堂
Time Limit: 1000 ms Case Time Limit: 1000 ms Memory Limit: 64 MB
Total Submission: 702 Submission Accepted: 110
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)
输入到文件结束
第一行: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 |
优化一下。其实我们不必开出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;
}