zoukankan      html  css  js  c++  java
  • 最大报销额【浙大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,其实就是要求掌握备忘录方法的基本精髓,有空还得多做点提高的题,比如上次热身赛的《合唱队形》。

  • 相关阅读:
    SGU 271 Book Pile (双端队列)
    POJ 3110 Jenny's First Exam (贪心)
    HDU 4310 Hero (贪心)
    ZOJ 2132 The Most Frequent Number (贪心)
    POJ 3388 Japanese Puzzle (二分)
    UVaLive 4628 Jack's socks (贪心)
    POJ 2433 Landscaping (贪心)
    CodeForces 946D Timetable (DP)
    Android Studio教程从入门到精通
    Android Tips – 填坑手册
  • 原文地址:https://www.cnblogs.com/Rafy/p/2388793.html
Copyright © 2011-2022 走看看