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

  • 相关阅读:
    Gradle Android Plugin 中文手册
    WxApiUtil.ts
    通过qrcodejs2和html2canvas把iframe中的内容生成带二维码的海报长图片
    TypeScript--类型声明
    ZJNU 2663
    ZJNU 2652
    etcd学习(7)-etcd中的线性一致性实现
    com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。
    帝国cms显示点击数比后台多1个的解决方法
    RedHat换源
  • 原文地址:https://www.cnblogs.com/Rafy/p/2388793.html
Copyright © 2020-2023  润新知