动态规划——一个神奇的算法类

当我第一次做到一个题,那是一个拥暴搜来解决的问题。原本只是想要简单的看一下贪心法的算法实现,没想到竟然挖出了真么个庞然大物。

在没有看到他的代码实现之前,我一直以为计算机只是一个可以大量快速计算,但却只能以二进制,傻傻的算的机器。我没要做的,便是把一切问题转化为二进制的计算形式,交给他来解决。因此在我的印象中,算法挺无趣的。而动态规划这种问题,显然是只有高智商,高情商的人类才能做得的高级思维活动啊。

知道了动态规划之后,我才第一次深刻认识到计算机算法的神奇与强大,虽然底层运算始终是0与1的加减替换,但简单的0与1,也可以构造出如此人性化的计算方式。

好的,感慨到此为止,话不多说,以下贴题。

SDNU--1033.采药

Description

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 

如果你是辰辰,你能完成这个任务吗?

Input

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间(1 <= t <= T)和这株草药的价值(1 <= v <= 100000)。

Output

输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

Sample Input

100 5
77 92
22 22
29 87
50 46
99 90

Sample Output

133
当然,你可以说以采药为题很东方,很玄幻(也许只是我个人感觉),但这是我遇到的最费解的题,因为他的动态。

首先题目给了你总时间T,和草药总数M,每株草药的价值和时间。当然数据不算很大,这个题考的不是时间复杂度,二是算法实现,基础的01背包。(感谢强大的百度)这个题因为出现了两个可供规划的量,让我想起了高中学的线性规划。对付这类题,你需要的是找到一种逻辑上比较好理解,又容易实现和具体操作的算法,来解决这种实际的问题。就在这时,我了解到了“背包”。

这是一个多么形象的比喻啊!

以下摘自百度百科————

背包问题(Knapsack problem)是一种组合优化的 NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,
我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学, 计算
复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为 决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年
由Merkel和Hellman提出的。
背包问题已经研究了一个多世纪,早期的作品可追溯到1897年 [1]   数学家托比亚斯·丹齐格(Tobias Dantzig,1884-1956)的早期作品 [2]   ,并指
的是包装你最有价值或有用的物品而不会超载你的行李的常见问题。
(其实我发现这些题都是和古现当代数学问题有关的)这是一个好用的模版,你完全可以代入采药。
而最关键的一部代码就在于————
f[j]=max(f[j-m[i]]+v[i],f[j]);

01背包的状态转换方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }


So simple, so powerful! 乍一看你或许会觉得这和递归有些相似,但事实上他们根本不是一回事。递归是机械的,死板的,而动态规划的奇妙,就在于动态。对于每一步,每一次甄选,都有两个方向,随数据的改变而改变。毕竟,在这种计算整体最优的题上,一步最优不是最优,在最后那一步达成之前,一切最优都是不确定的。
以我现在的水平,还不能很清晰的在脑海中重现他在计算机中的每一步实现与运算,但我可以认识到这种算法有大用。
具体到这个题上来,在时间T内,采得最大价值药草。一看就知道这个问题很复杂,就算是用人脑考虑,数据稍大也很麻烦。既然如此,我们只好尝试倒推,来简化逻辑。假设f[i,T]最大价值已经达成,那么对于任意一个草药,我们有2种选择,放或者不放,而一旦背包中一种草药确定放入,那么背包中其他草药所用最大时间  t 也就确定,在这个时间内最大价值f[i-1, t] 也就确定。对于这个草药,我们只要判断一下f[i-1,t] +v 与 f[i-1, T]的大小,就可以确定一个或除去一个草药。n次之后,得出结果。逻辑如此,剩下的便是准备工作。
在以上逻辑中,我们需要对每一个草药进行判断,和几乎每一个时间内的最大价值连递得出,所以,外层n个草药循环,每个草药内层每个时间内判断是否放入。因为时间量不同,对于每个草药是否放入都有影响,所以我们只好把每个时间点上都计算出,毕竟动态规划靠的就是数据量大,复杂度高,精细判断。
以下是在下的代码,记得好像是借鉴了网上大佬的典例,毕竟我只是新手。
#include<cstdio>
#include<iostream>
using namespace std;
#define N 1005
#define M 105


int t,n;
int v[M],m[M],f[N];
int main()
{
    int i,j;
    scanf("%d %d",&t,&n);
    for(i=1;i<=n;i++)
        scanf("%d %d",&m[i],&v[i]);
    for(int i=1;i<=n;i++)
        for(j=t;j>=m[i];j--)
            f[j]=max(f[j-m[i]]+v[i],f[j]);//核心core
    printf("%d",f[t]);
    return 0;
}
最精炼的代码就是最好的代码,在我没看背包算法自己尝试的那些日子里,我还以为会多么繁杂,然而,just so-so 。
这是个动态规划入门题,变式很多难度也更大,ACM之路与君共勉。
So far,thanks for your reading.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值