动态规划(面向过程)

1 基本概念

如图
AG最短路线
动态规划

1.1 阶段

解决过程中若干个相互联系版块,如图中A,B,C…

1.2 状态

某一阶段的出发点,如图中B1,C2,G等

1.3 决策

解决过程中的选择,如从A到B的决策是5(3)我没去做

1.4 策略

所有决策的有序集合,如图A->B1->->F1->G就是一种决策

1.5 状态转移方程

此状态(k)到(k+1)状态的的演变规律

1.6 例题详解

数塔问题:
给定其层数与每层的i个数,求从首层到底层的最大数字和(只能往下走)
1.其中数塔的层数就是此题的各阶段
2.其中每层的每个数字就是一个状态
3.其中决策从倒数第二层出发,和下一层的相邻两个数中最大的数相加
4.其中策略是从底层首层最大和
5.其中状态转移方程a[i][j]=maxa[i+1][j]a[i+1][j+1]

知道了这些,代码就呼之欲出了

#include <iostream>
using namespace std;
int main() {
    int n;
//输入层数
    cin>>n;
    int a[n+1][n+1];
    for(int i=1;i<=n;i++) {//阶段
        for(int j=1;j<=i;j++) {//状态
        //输入数塔
            cin>>a[i][j];
        }
    }
//设计算法
    for(int i=n-1;i>0;i--)
        for(int j=1;j<n;j++)
        //挨个转移状态
            a[i][j]+=max(a[i+1][j],a[i+1][j+1]);
//输出答案
    cout<<a[1][1];
	return 0;
}

2 背包问题

给定容量大小为V的书包,输入N件物品的体积与价值,求在容量允许内可取得的最大价值
在这里插入图片描述

2.1 01背包

指物品只有拿与不拿两种选择
那么,在这种情况下的状态如何转移呢
因为一个物品的选择只有拿与不拿两种,所以决策是不拿的价值与拿后的价值最大值
定义Bag[n][V] ,则有Bag[i][j] = max {Bag[i - 1][j] , Bag[i - 1] [j - v[i]] + c[i] }

#include <iostream>
using namespace std;
int main() {
//背包容量和物品总数
    int V,n;
    cin>>V>>n;
//物品体积与价值
    int v[n+1],c[n+1];
    int Bag[n+1][V+1];
    for(int i=1;i<=n;i++)
        cin>>v[i]>>c[i];
    for(int i=0;i<=V;i++)
        Bag[0][i]=0;
    for(int i=1;i<=n;i++)
        for(int j=V;j>=0;j--) {
        //可不可以拿
        	if(j>=v[i]) {
        	//状态转移方程
                Bag[i][j]=max(Bag[i-1][j],Bag[i-1][j-v[i]]+c[i]);
            }
            else {
                Bag[i][j]=Bag[i-1][j];
            }
        }
    cout<<Bag[n][V];
    return 0;
}

二位数组存储各状态虽然方便,但当物品数量过多时,可以将Bag[n][V]改为Bag[V]
则状态转移方程是Bag[j]=max{ Bag[ j ] , Bag[ j - v[ i ] + c[ i ] ] }

#include <iostream>
using namespace std;
int main() {
//背包容量和物品总数
    int V,n;
    cin>>V>>n;
    int v[n+1],c[n+1];
    int Bag[V+1];
    for (int i=1;i<=n;i++)
        cin>>v[i]>>c[i];
    for (int i=0;i<=V;i++)
        Bag[i]=0;
    for (int i=1;i<=n;i++)
        for (int j=V;j>=v[i];j--)
        //新的方程
            Bag[j]=max(Bag[j],Bag[j-v[i]]+c[i]);
    cout<<Bag[V];
    return 0;
}

总的来说01背包问题是最基本的背包问题,其他类型的背包问题也可以变化成01背包解决

2.2 完全背包

物品有无数件
从01的状态转移方程Bag[i][j]=max{Bag[i - 1][ j ],Bag[i - 1][j - v[ i ] ] + c[ i ] }
k模拟取得的数,
得出Bag[ i ][ j ]=max{Bag [ i - 1 ] [ j ],Bag [ i - 1 ] [ j - k * v [ i ] ]+k * c[ i ]}
稍稍简化,循环要从0到V顺序循环才可以,不能从V到0
因为从V到0是为了保证没有重复选一件物品,但是完全背包问题中的物品可以重复取用,所以要从0到V循环
Bag[ j ] = max { Bag[ j ] , Bag[ j - v[ i ] ] + c[ i ] }

#include <iostream>
using namespace std;
int main() {
//背包容量和物品总数
    int V,n;
    cin>>V>>n;
    int v[n+1],c[n+1];
    int Bag[V+1];
    for (int i=1;i<=n;i++)
        cin>>v[i]>>c[i];
    for (int i=0;i<=V;i++)
        Bag[i]=0;
    for (int i=1;i<=n;i++)
    //稍微简化,从能装开始循环
        for (int j=v[i];j<=V;j++)
            Bag[j]=max(Bag[j],Bag[j-v[i]]+c[i]);
    cout<<Bag[V];
    return 0;
}

2.3 多重背包

物品有指定数量个
还是从01背包的状态转移方程
Bag[i][j]=max{Bag[i - 1][ j ],Bag[i - 1][j - v[ i ] ] + c[ i ] }
k模拟每种物品的数量
Bag[ i ][ j ]=max{Bag [ i - 1 ] [ j ],Bag [ i - 1 ] [ j - k * v [ i ] ]+k * c[ i ]}

#include <iostream>
using namespace std;
int main()
{
    int V,N;
    cin>>V>>N;
    int v[N+1],c[N+1],n[N+1];
    int Bag[V+1];
    for (int i=1;i<=N;i++)
        cin>>v[i]>>c[i]>>n[i];
    for (int i=0;i<=V;i++)
        Bag[i]=0;
	for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)
            for(int k=0;k<=n[i];k++)
                if(j>=k*v[i])
                    Bag[j]=max(Bag[j],Bag[j-k*v[i]]+k*c[i]);
    cout<<Bag[V];
    return 0;
}

但是,我们可以将N种物品转化为n[i]个独立物品
就变成01背包问题了

#include <iostream>
#define maxn 3005
using namespace std;
int main()
{
    int V,tmpn,N=0;
    cin>>V>>tmpn;
    int x,y,n,tmp;
    int v[maxn],c[maxn];
    int Bag[V+1];
    for (int i=1;i<=tmpn;i++) {
        cin>>x>>y>>n;
        tmp=1;
        while(n>=tmp) {
            v[++N]=x*tmp;
            c[N]=y*tmp;
            n-=tmp;
            tmp*=2;
        }
        v[++N]=x*n;
        c[N]=y*n;
    }
    for (int i=0;i<=V;i++)
        Bag[i]=0;
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)
                    Bag[j]=max(Bag[j],Bag[j-v[i]]+c[i]);
    cout<<Bag[V];
    return 0;
}

2.4 小结

此外,还有三种背包混合二维费用背包,分组背包,附赠背包,以及方案总数等等问题,但他们都由01背包发展而来,这里抛砖引玉,望大家举一反三,多多思考,谢谢!

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页