最大报销额【浙大07年机试真题】初学动态规划

今天建立了自己的新博客,只是因为cnblogs更适合于保存曾经写过的代码,以后A题想记录下来的点点滴滴都会写在这上面,希望自己能一步步坚持下去...

以前接触过DP的题,但都是知难而退,大二有过动态规划的课,但貌似毫无例外的逃掉了,昨晚重拾算法书,好好看了看,有点领悟,于是把这道入门级别的动态规划题A了。

其实动态规划的基本思想很简单,就是分解问题时,重复的子问题个数是多项式级别的,但是如果用上递归求解,则规模瞬间增至指数级别,在OJ上常常TLE,于是,动态规划引入了备忘录方法,简单说来,就是建立一个2维表格,记录下每次求解的子问题答案,到需要这个答案时,可以用2维表的纵横坐标随机查找到,这个查找时间是O(1),相对于递归,不但省去了重复计算,而且查找答案的时间也降至O(1)

以下是前段时间写的DP思路,感谢同样报考浙大的朋友发现bug,这个思路需要修改,但暂时还不知道从何改起,希望知道的朋友能留言释惑:

 1 /*对于一个待报销支票序列a(1~n),建立备忘录m[1~n][1~n], m[i][j]的最初含义是指从a[i]连续加到a[j]得到的和,计算方式显然是:m[i][j] = m[i][j - 1] + a[j],但这有一个条件,得到的和要小于报销额上限Q,如果测试m[i][j-1] + a[j]结果大于Q时,则m[i][j]被赋值为与m[i][j-1]相等,其含义是将最近满足条件的和保留至m[i][j]中,以便下次判断m[i][j-1]+a[j]时,不至于m[i][j-1]为空值,这就是DP解这道题的基本思想,核心代码如下:*/
2
3
4 ans = 0;//记录当前寻找到的最大报销额
5 for(i = 0; i < length; i ++)
6 {
7 m[i][i] = a[i];
8 if(a[i] > ans)
9 {
10 ans = a[i];
11 }
12 //printf("%lf ", m[i][i]);
13 }//对备忘录m进行初始化,依据m的本意,得m[i][i]=a[i];
14 for(i = 0; i < length; i ++)
15 {
16 for(j = i + 1; j < length; j ++)
17 {
18 if(m[i][j - 1] + a[j] < q)
19 {
20 m[i][j] = m[i][j - 1] + a[j];
21 if(ans < m[i][j])
22 {
23 ans = m[i][j];
24 }
25 }
26 else if(m[i][j - 1] + a[j] > q)
27 {
28 m[i][j] = m[i][j - 1];//问题就出在这个if块里,但是不知道该如何修改
29 }
30 else
31 {
32 ans = q;
33 break;
34 }
35 }
36 }


下面是用0-1背包解的,转移方程是m[j] = max{m[j], m[q - a[j]] + a[j]}. m[j]的意思是对于给出的支票序列,如果限额是j,计算出的最大报销额。所以程序最后输出m[q]即可,但是题目是2位小数的浮点,所以*100转化为int,这样也带来了内存开销.


 1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int main()
5 {
6 double q;
7 int qq;
8 int *a;
9 int n;
10 int i, total, j, length;
11 char order;
12 double data;
13 double temp1, temp2, temp3;
14 double temp;
15 int mark;
16 int *m;
17
18 while(scanf("%lf %d",&q,&n) != EOF && n)
19 {
20 a = (int *)malloc(sizeof(int) * n);
21 j = 0;
22 for(i = 0; i < n; i ++)
23 {
24 mark = 1;
25 scanf("%d",&total);
26 temp = 0;
27 temp1 = 0;temp2 = 0;temp3 = 0;
28 while(total --)
29 {
30 scanf(" %c:%lf",&order,&data);
31
32 temp += data;
33 if(order == 'A') temp1 += data;
34 else if(order == 'B') temp2 += data;
35 else if(order == 'C') temp3 += data;
36 else
37 {
38 mark = 0;
39 }
40 }
41 if(temp1 > 600 || temp2 > 600 || temp3 > 600 || temp > 1000 || temp > q)
42 {
43 mark = 0;
44 }
45 if(mark == 1)
46 {
47 a[j] = (int)(temp * 100);
48 j ++;
49 }
50 }
51 length = j;
52 qq = (int)(100 * q);
53 m = (int *)malloc((qq + 1) * sizeof(int));
54 for(i = 0; i <= qq; i ++)
55 {
56 m[i] = 0;
57 }
58 for(i = 0; i < length; i ++)
59 {
60 for(j = qq; j >= a[i]; j --)
61 {
62 if(m[j] < m[j - a[i]] + a[i])
63 {
64 m[j] = m[j - a[i]] + a[i];
65 }
66 }
67 }
68 printf("%.2lf\n", m[qq] / 100.0);
69 }
70 return 0;
71 }
72
73 /**************************************************************
74 Problem: 1025
75 User: qiushuiruyan
76 Language: C
77 Result: Accepted
78 Time:40 ms
79 Memory:21700 kb
80 ****************************************************************/


这道题只是一个初级难度的DP,其实就是要求掌握备忘录方法的基本精髓,有空还得多做点提高的题,比如上次热身赛的《合唱队形》。

原文地址:https://www.cnblogs.com/Rafy/p/2388793.html