从暴力递归到动态规划的转换(推荐)

从暴力递归到动态规划

给一串数字,返回其能否转换成IP地址形式(IP地址的正确形式)。

如110.125.10.5

int P(i,p)

假设0...i-1都已经转成合法的IP段了,组成的IP段数是p个。

则有:

递归版本:

/**
 * Created by YangGang on 2017/9/6.
 */
public class ConvertStringToIP {

    public static int convertNum1(String str){
        //IP地址包括的数字个数范围在4~12
        if(str==null||str.length()<4||str.length()>12){
            return 0;
        }
        char [] chars=str.toCharArray();
        return process(chars,0,0);
    }

    public static int process(char [] chars,int i,int parts){
        //终止条件,一个是遍历结束,另一个是划分大于4部分,而IP只有4部分排除
        if(i>chars.length||parts>4){
            return  0;
        }
        //终止条件:已经到了结尾,看看是否正好分成4部分,若正好分成4部分则成功,否则失败
        if(i==chars.length){
            return parts==4?1:0;
        }
        //如果把自己作为一个部分,合法的个数
        int res=process(chars,i+1,parts+1);
        //如果当前字符是0,只能够把当前自己作为一段,因为IP中不可能出现012这种情况
        if(chars[i]=='0'){
            return res;
        }
        //如果当前字符不是0,则有可能是当前字符和下一字符一起构成一个部分。
        res+=process(chars,i+2,parts+1);
        //当前字符不是0,和下两个字符一起构成一部分,注意IP一个段只能是0-255
        if(i+2<chars.length){
            int sum=(chars[i]-'0')*100+(chars[i+1]-'0')*10+(chars[i+2]-'0');
            if(sum<255)
            {
                return res+process(chars,i+3,parts+1);
            }
            else {
                return res;
            }
        }
        else {
            return res;
        }
    }
}

递归:

第一步:了解BaseCase(不需要依赖任何,自己就知道自己的值)

第二步: 建立上下文关系

第三步:找好递归出口(边界条件)


递归改写:

首先看process(char [] chars,int i,int parts)函数

如果i和parts确定了,返回值就确定了。

所以可以建立一个二维表:(i,parts---唯一的返回值)

自然要关注parts的取值,parts的空间可以开成6个:0,1,2,3,4,>4(5) ;

i的取值[0,n+2]。


则二维表如下:

   0 1 2 3 4 5

0

1

2

.

.

.

n-1

n

n+1

n+2


(0,0)可能依赖(1,1)、(2,1)、(3,1)

1pg


先确定最右边的值,之后就从右边往左来。

然后从BaseCase转化,先填写好。

1pg


填写好后,再按照递归指示顺序把表剩余部分填写完毕。

从下往上,从右往左填写。


1pg


实际上并不需要都去填写。

1pg


看看哪些位置是不用算的,在for循环中控制一下,可以进一步减少计算。


改写成的DP代码如下:

public static int convertNum2(String str){
        if(str==null||str.length()<4||str.length()>12){
            return 0;
        }
        char [] chars=str.toCharArray();
        int size=chars.length;
        int [][]dp=new int [size+3][5];
        //初始值
        dp[size][4]=1;
        for(int parts=3;parts>=0;parts--){
            //因为IP每个段最多3位(i=Math.min(size-1,parts*3))
            for(int i=Math.min(size-1,parts*3);i>=parts;i=Math.min(i-1,parts*3)){
                dp[i][parts]=dp[i+1][parts+1];
                if(chars[i]!=0){
                    dp[i][parts]+=dp[i+2][parts+1];
                    if(i+2<chars.length){
                        int sum=(chars[i]-'0')*100+(chars[i+1]-'0')*10+(chars[i+2]-'0');
                        if(sum<256){
                            dp[i][parts]+=dp[i+3][parts+1];
                        }
                    }
                }
            }
        }
        return dp[0][0];
    }







先算第3列,再算第2列,然后算第1列,最后算第0列。


刨除那些不需要算的,

1pg





因为p是有限的,所以可以蜕化为O(n)


所以把递归转换成动态规划的步骤是:

第一步:确定解空间

第二步:看看哪些事都不依赖的(BaseCase)设好值

第三步:看看哪个状态是所求的状态

最后一步:确定计算顺序

具体到dp的决策,把递归中的决策抄下来就行了。










  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值