动态规划面试题--数字转化机

最近编程题目都是DP,我把今天这个题给大家做个解释和实现。大家好好体会练习,遇到最优解问题(最小/最大)时,优先DP解法。

概念

一般求最优解通常使用DP(Dynamic Programming)。

DP主要两个要素,有三种解法。

  • 两要素
    • 定义状态
    • 定义状态转化方程

状态和状态转化方程最关键也比较困难。状态转化方程包含边界。有时称为三要素,是把边界单独分离出来,与状态和状态转化方程并列。

  • 三种解法
    • 递归法
      递归法是直接使用状态转化方程,实际算不上DP,而是减而治之,在重叠子问题时,效率低下(时间复杂度O(2^n))。
    • 备忘录法
      备忘录法实际上是对递归法优化,通过增加存储空间保存中间运算值,减少递归运算次数。
    • 表格法
      这是最正统的DP,递归法和备忘录法都是自顶而下的递归,表格法是自底而上的迭代。DP本质是把递归变为迭代,降低时间复杂度。

题目

时间限制:(每个case)2s,空间限制:128MB
小Q从牛博士那里获得一台数字转化机,这台数字转化机必须同时输入两个整数ab,并且这台数字转化机有一个红色按钮和一个蓝色按钮。

  • 当按下红色按钮,两个数字同时加一。
  • 当按下蓝色按钮,两个数字同时乘二。

小Q现在手中有四个整数,abAB,他希望将输入的两个整数ab变成ABa对应Ab对应B)。因为牛博士允许小Q使用数字转化机时间有限,所以,小Q希望按动按钮的次数越少越好,请你帮帮小Q吧。

  • 输入:
    输入包括一行,一行中包含四个整数a,b,A,B。(1<=a,b,A,B<=10^9)
  • 输出:
    如果小Q能够完成转换,输出最少需要按动按钮的次数,否则输出-1
  • 样例输入:
100 1000 202 2002
  • 样例输出:
2

分析

  • 定义状态
    transfer(first,last)表示从数字first转化到数字last最少转化次数
  • 定义状态转移方程
    自顶而下分析,最后一步转化无非两种情况,前一步结果*2或者+1,那么前一次结果为last/2或者last-1。最后一步经历的转化次数无非是transfer(first,last/2)或者transfer(first,last-1)。显然可得,取最小的transfer(first,last) = min(transfer(first,last/2),transfer(first,last-1))+1
    递归边界最终会有两种情况,一种是first刚好与last相同,说明这是可以转换。一种是first>last,说明不能完成转化。
    结合上面两点状态转移方程如下所示:

解决

为了使代码更加简洁地表示解决思路,中间未添加参数检查处理。

测试主函数

本题测试函数非常简单只是终端输入输出操作。

int main(){
    int a(0),b(0),A(0),B(0);
    cin >> a >> b >> A >> B;
    int res1 = transfer(a,A);
    int res2 = transfer(b,B);
    if(res1 == res2 and -1 != res1){
        cout << res1;
    }else{
        cout << -1;
    }
}

实现转换函数transfer()

  • 递归法
    直接把状态转换函数翻译成代码即可。
int transfer(int first,int last){
    if(first == last) return 0; // 可以完成转换
    if(first > last) return -1; // 不能完成转换
    int res1 = transfer(first,last/2);
    int res2 = transfer(first,last-1);
    if(-1 == res1 and -1 == res2){
        return -1;
    }else if(-1 == res1){
        return res2+1;
    }else if(-1 == res2){
        return res1+1;
    }else{
        return min(res1,res2)+1;
    }
}

注意,转化失败的分支不能参与最小转化次数比较的。程序需要增加这些处理。

  • 备忘录法
    在递归过程中,记录计算结果,下次出现时不需要重新计算,直接从存储结果中获取即可。
int transfer(int first,int last,int buf[]){
    if(first == last) return 0; // 可以完成转换
    if(first > last) return -1; // 不能完成转换
    if(-1 != buf[last]){
        return buf[last];
    }
    int res1 = transfer(first,last/2,buf);
    int res2 = transfer(first,last-1,buf);
    if(-1 == res1 and -1 == res2){
        return -1;
    }else if(-1 == res1){
        buf[last] = res2+1;
    }else if(-1 == res2){
        buf[last] = res1+1;
    }else{
        buf[last] = min(res1,res2)+1;
    }
    return buf[last];
}
int transfer(int first,int last){
    int buf[last+1];
    fill_n(buf,last+1,-1);
    return transfer(first,last,buf);
}

注意这里定义一个数组,数组的下标表示last,数组值表示从数字first转化到数字last最少转化次数。数组需要初始化为无效值-1,用于判断是否已经计算过。

  • 表格法
    表格法用自底而上的迭代代替自顶而下的递归。
int transfer(int first,int last){
    int buf[last+1];
    fill_n(buf,last+1,0);
    for(int i=first+1;i<=last;i++){
        if(i/2 >= first and i-1 >= first){
            buf[i] = min(buf[i/2],buf[i-1])+1;
        }else if(i/2 >= first){
            buf[i] = buf[i/2]+1;
        }else if(i-1 >= first){
            buf[i] = buf[i-1]+1;
        }else{
            buf[i] = -1;
        }
    }
    return buf[last];
}

这里数组表示含义与备忘录法一致的,虽然会有一定空间浪费,时间复杂度,已经从O(2^n)降低到O(n)

通常有时间限制的DP,优先使用表格法,备忘录法在一些情况会比表格法快,基本不使用递归法。

使用回溯法可以把表格法和备忘录法获得的存储中间值,转化成获得最优解具体的操作,例如本题中,小Q究竟是依次按了哪些按钮获得last值。有能力的童鞋,可以自己练习解决。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,740评论 0 33
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,270评论 0 18
  • 回溯算法 回溯法:也称为试探法,它并不考虑问题规模的大小,而是从问题的最明显的最小规模开始逐步求解出可能的答案,并...
    fredal阅读 13,632评论 0 89
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 矮个子女生也能穿长裙吗? 不要再给自己贴标签了! 矮个姑娘穿长裙同样HOLD得住。 夏天快到了,穿裙子的那颗心开始...
    拍范阅读 402评论 0 0