[PKU暑课笔记] 动态规划(三) 最佳加法表达式 百练2755 POJ3624背包问题

来源:互联网 发布:淘宝网怎么装修 编辑:程序博客网 时间:2024/05/21 07:09

OpenJudge 最佳加法表达式 http://cxsjsxmooc.openjudge.cn/2017t2springhw5/3/

思路:

假定数字串长度是n,添完加号后,表达式的最后一个加号添加在第 i 个数字后面,

那么整个表达式的最小值,等于在前 i 个数字中插入 m – 1个加号所能形成的最小值,加上第 i + 1到第 n个数字所组成的数的值(i从1开始算)。

V(m,n)表示在n个数字中插入m个加号所能形成的表达式最小值,

Num(i,j)表示从第i个数字到第j个数字所组成的数。数字编号从1开始算。此操作复杂度是O(j-i+1),可以预处理后存起来。那么:

if(m=0)    V(m,n)=n个数字构成的整数;else if (n<m+1)    V(m,n)=∞;else    V(m,n)=min{V(m-1,i)+Num(i+1,n)}(i=m...n-1);


OpenJudge2755 神奇的口袋 http://bailian.openjudge.cn/practice/2755/

解法:

1)枚举:枚举每个物品是选还是不选,共2^20种情况

2)递归

#include<iostream>using namespace std;int a[30];int n;int solve(int w,int k){//从前k种物品中选择一些,凑成体积w的做法数目    if(w==0)return 1;    if(k<=0)return 0;    return solve(w,k-1)+solve(w-a[k],k-1);}int main(){    cin>>n;    for(int i=1;i<=n;++i)        cin>>a[i];    cout<<solve(40,n);    return 0;}
3)动归
#include<iostream>#include<cstring>using namespace std;int a[40];int n;int solve[50][40];//表示从前j种物品里凑出体积i的方法数int main(){    cin>>n;    memset(solve,0,sizeof(solve));    for(int i=1; i<=n; ++i)    {        cin>>a[i];        solve[0][i]=1;    }    solve[0][0]=1;    for(int w=1; w<=40; ++w)        for(int k=1; k<=n; ++k)        {            solve[w][k]=solve[w][k-1];            if(w-a[k]>=0)                solve[w][k]+=solve[w-a[k]][k-1];        }    cout<<solve[40][n];    return 0;}


POJ3624 Charm Bracelet http://poj.org/problem?id=3624

题意:有N件物品和一个容积为M的背包。第i件物品的体积w[i],价值是d[i]。

求解将哪些物品装入背包可使价值总和最大。

每种物品只有一件,可以选择放或者不放(N<=3500,M <= 13000)。

思路:

F[i][j]:取前i种物品,使它们总体积不超过j的最优取法取得的价值总和。

边界:if (w[1] <= j) F[1][j]=d[1]; else F[1][j] = 0;

递推:F[i][j] = max(F[i-1][j],F[i-1][j-w[i]]+d[i])

取或不取第i种物品,两者选优(j-w[i] >= 0才有第二项)

本题如用记忆型递归,需要一个很大的二维数组,会超内存。

注意到这个二维数组的下一行的值,只用到了上一行的正上方及左边的值,

因此可用滚动数组的思想,只要一行即可。