动态规划

来源:互联网 发布:徐州淘宝产业链 编辑:程序博客网 时间:2024/05/22 15:44

本文章参考《挑战程序设计竞赛》的2.3节“记录结构再利用的动态规划”。

01背包问题

有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

 for example:n=4,(w,v)={(2,3),(1,2),(3,4),(2,2)},W=5。

对于解决这个问题,先尝试用最朴素的办法针对每个物品是否放入背包进行搜索试试。代码如下:

<span style="font-family:Microsoft YaHei;">//输入int n,W;int w[MAX_N],v[MAX_N];//从第i个物品开始挑选总重小于j的部分int rec(int i,int j){    int res;    if(i==n)    //已经没有剩余物品了        res=0;    else if(j<w[i])   //无法挑选这个物品        res=rec(i+1,j);    else    //挑选和不挑选两种情况都进行尝试        res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);    return res;}void solve(){    printf("%d\n",rec(0,W));}</span>
这种方法的搜索深度是n,且每一层搜索都需要两次分支,最坏就需要O(2^n)的时间。递归调用情况如图所示:

从图中可以看出,rec以(3,2)为参数调用了两次。如果参数相同,返回的结果也应该相同,于是出现了重复计算的情况。现在把第一次计算时的结果记录下来,省略掉重复计算,代码如下:
<span style="font-family:Microsoft YaHei;">int dp[MAX_N+1][MAX_W+1];   //记忆化数组int rec(int i,int j){    if(dp[i][j]>=0) //已经计算过的话直接使用之前的结果        return dp[i][j];    int res;    if(i==n)    res=0;    else if(j<w[i]) res=rec(i+1,j);    else    res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);    //将结果记录在数组中    return dp[i][j]=res;}void solve(){    //用-1表示尚未计算过,初始化整个数组    memset(dp,-1,sizeof(dp));    printf("%d\n",rec(0,W));}</span>
dp[i][j]:=从第i个物品开始挑选总重小于j时,总价值的最大值
dp[n][j]=0

根据上述递推公式直接将各项的值计算出来。代码如下:
<span style="font-family:Microsoft YaHei;">int dp[MAX_N+1][MAX_W+1];   //DP数组void solve(){    for(int i=n-1;i>=0;i--)    {        for(int j=0;j<=W;j++)        {            if(w[i]>j)                dp[i][j]=dp[i+1][j];            else                dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);        }    }    printf("%d\n",dp[0][W]);}</span>



0 0
原创粉丝点击