【算法学习】切割木棍问题——动态规划

问题描述:

假设,有一条长度为n的木棍,已知木棍的销售价格Pi与木棍长度i有关,i = 1,2,3,...n.问,怎样切割能获得最大收益。

长度为0的木棍收益肯定是0了,即profit[0] = 0.

切割长度(seg)12345678910
销售价格(pi)1589101717202430

对于长度为n的木棍,他的递推关系是:profit[n] = max(pi[i] + profit[length - seg[i]]), 其中i = 1,2,3,...n;

暴力解决方法:

int Cut_Common(int seg[], int pi[], int arr_len, int source_len)
{
    if (source_len == 0)
        return 0;
	int tmp = -1;
	for (int i = 0; i < arr_len; ++i)
	{
        if (source_len - seg[i] >= 0)
		    tmp = max(tmp, pi[i] + Cut_Common(seg, pi, arr_len, source_len - seg[i]));
	}
	return tmp;
}

这样的解法, 会发生多次相同的自问题求解,故效率非常差。联想每次调用产生一个树节点,叶节点代表了结束递归,O(2^n)的时间复杂度

为了解决对相同自问题的多次求解,故可用一个数组储存自问题的解,下次需要时就不用再从新算。

动态规划——自底向上方法

int _Cut_Dynamic_DownToTop(int seg[], int pi[], int arr_len, int length, int dump[])
{
    int tmp;
    dump[0] = 0;
    for (int i = 1; i <= length; ++i)
    {
        tmp = -1;   
        for (int j = 0; j < arr_len; ++j)
        {
            if (i - seg[j] >= 0)
                tmp = max(tmp, pi[j] + dump[i - seg[j]]);               
        
        }
        dump[i] = tmp;

    }
    return dump[length];
}

int Cut_Dynamic_DownToTop(int seg[], int pi[], int arr_len, int length)
{
    int *dump = (int *)malloc(sizeof(int)*length + 1);
    int tmp = _Cut_Dynamic_DownToTop(seg, pi, arr_len, length, dump);
    free(dump);
    return tmp;
}
解决的主要思路是,先求解长度为1的最大收益,再到2,3.....一直到n,而每次求解的解都储存起来,时间复杂度为O(nm),空间复杂度为O(n)。


自底向上的解法仍有瑕疵,那就是在求解给定问题时,有些较小问题的解常常是不必须的,而自顶向下 + 记忆功能 解决了这个问题。

动态规划——自顶向下(+记忆功能):

int _Cut_Dynamic_TopToDown(int seg[], int pi[], int arr_len, int length, int dump[])
{
    if (dump[length] >= 0)
        return dump[length];
    int tmp = -1;
    for (int i = 0; i < arr_len; ++i)
    {
        if (length - seg[i] >= 0)
            tmp = max(tmp, pi[i] + _Cut_Dynamic_TopToDown(seg, pi, arr_len, length-seg[i], dump));
    }
    dump[length] = tmp;
    return dump[length];
}

int Cut_Dynamic_TopToDown(int seg[], int pi[], int arr_len, int length)
{
    int *dump = (int *)malloc(sizeof(int)*length+1);
    for (int i = 0; i <= length; ++i)
    {
        dump[i] = -1;
    }
    dump[0] = 0;
    int tmp = _Cut_Dynamic_TopToDown(seg, pi, arr_len, length, dump);
    free(dump);
    return tmp;
}

注意到,自顶向下 和 暴力解决的 细微差别,自顶向下记录了每一步的结果(这就是记忆功能),而暴力解决方法总是重新算结果。








评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值