动态规划基础

一、线性dp:

(1)数字三角形模型

数字三角形如果数据中有负数,则需要把边界初始化为很小的数;而且对于第一行(即第一个数),应该直接赋值,如果直接取max的话会取到负数

数字三角形的状态转移方程为:

f[i][j]=max(f[i-1][j],f[i-1][j-1]);

也可以倒序dp:

下面的题目代码注释写的很详细了:        AcWing 898. 数字三角形 - AcWing(已做笔记)

(2)最长上升子序列模型:

int main()
{
    cin >> n;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=1;i<=n;i++) f[i]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j

贴几道题目:        AcWing 895. 最长上升子序列 - AcWing (已做笔记 )

 AcWing 1017. 怪盗基德的滑翔翼 - AcWing               AcWing 1010. 拦截导弹 - AcWing

 AcWing 1014. 登山 - AcWing                   AcWing 1012. 友好城市 - AcWing

AcWing 482. 合唱队形 - AcWing            AcWing 1016. 最大上升子序列和 - AcWing

                                                                           

(3)最长公共子序列模型:

详细的说明在题目代码注释中

int main()
{
    int n,m;
    cin >> n >> m;
    string a,b;
    cin >> a >> b;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            f[i]=max(f[i-1][j],f[i][j-1]);
            if(a[i-1]==b[j-1]) f[i]=max(f[i],f[i-1][j-1]+1);
        }
    }
    return 0;
}

贴一道题目:        AcWing 897. 最长公共子序列 - AcWing (已做笔记)

(4)背包问题:

(1)01背包

题目:        AcWing 2. 01背包问题 - AcWing(已做笔记)

01背包只有选和不选两种情况,并且每种物品只有1个

二维数组写法:

#include 
#include 
#include 
using namespace std;
const int N=1010;
int v[N],w[N],f[N][N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin >> v[i] >> w[i];
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
            else f[i][j]=f[i-1][j];//必须要从上一层获取数据
        }
    }
    cout << f[n][m];
    return 0;
}

滚动数组(一维)写法:

#include 
#include 
#include 
using namespace std;
const int N=1010;
int v[N],w[N],f[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin >> v[i] >> w[i];
    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){//倒序,保证只取一次(保证前面的没有被计算过)
            f[j]=max(f[j],f[j-v[i]]+w[i]);//直接覆盖即可,因为是滚动数组,保留下来的数据就是上一层的,所以无需从上一层获取数据
        }
    }
    cout << f[m];
    return 0;
}

(2)完全背包

题目:        AcWing 3. 完全背包问题 - AcWing(已做笔记)

完全背包的每种物品都有无限件

滚动数组写法:

#include 
#include 
#include 
using namespace std;
const int N=1010;
int v[N],w[N],f[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin >> v[i] >> w[i];
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){//正序,保证每一种物品可以取多次(保证前面的已经被计算过,可以累加)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout << f[m];
    return 0;
}

(3)多重背包

题目:        AcWing 4. 多重背包问题 - AcWing(已做笔记)

朴素滚动数组写法:(转换成完全背包01背包

#include 
#include 
#include 
using namespace std;
const int N=1010;
int v[N],w[N],s[N],f[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin >> v[i] >> w[i] >> s[i];
    for(int i=1;i<=n;i++){
        if(v[i]*s[i]>=m){//装不完就转换成完全背包
            for(int j=v[i];j<=m;j++){
                f[j]=max(f[j],f[j-v[i]]+w[i]);
            }
        }else{//装的完就转换成01背包
            for(int j=m;j>=v[i];j--){
                for(int k=s[i];k>=0;k--){//遍历一下每种物品选几件(也可以不选)
                     if(j>=k*v[i]) f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
                }
            }
        }
    }
    cout << f[m];
    return 0;
}

 二进制优化
二进制优化的原理是利用小于等于当前值的 二的次方的数可以组合成任意小于等于当前值的数,然后对这些数采用01背包的策略

#include 
#include 
#include 
using namespace std;
const int N=1010;
int v[N],w[N],f[N],cnt;//cnt表示新划分的物品的总数量
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        int a,b,c;//a表示物品的体积,b表示物品的价值,c表示物品的数量
        cin >> a >> b >> c;
        int k=1;
        while(k<=s){//二进制优化
            cnt++;
            v[cnt]=k*a;
            w[cnt]=k*b;
            c-=k;
            k*=2;
        }
        if(c>0){
            cnt++;
            v[cnt]=c*a;
            w[cnt]=c*b;
        }
    }
    for(int i=1;i<=cnt;i++){//枚举cnt件新物品(是cnt件,不是n件)
        for(int j=m;j<=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout << f[m];
    return 0;
}

(4)分组背包

题目:        AcWing 9. 分组背包问题 - AcWing(已做笔记)

二维数组写法:

#include 
#include 
#include 
using namespace std;
const int N=1010;
int w[N][N],v[N][N],f[N][N],s[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        cin >> s[i];
        for(int k=1;k<=s[i];k++) cin >> v[i][k] >> w[i][k];
    }
    for(int i=1;i<=n;i++){//01背包
        for(int j=0;j<=m;j++){
            for(int k=0;k<=s[i];k++){//从0开始,已经包含了不选的情况
                if(j>=v[i][k]) f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
                //else f[i][j]=f[i-1][j];  这一行不能写,因为上面的枚举已经包含了不选的情况了
            }
        }
    }
    cout << f[n][m];
    return 0;
}

滚动数组写法:

#include 
#include 
#include 
using namespace std;
const int N=110;
int f[N],w[N],v[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++){//遍历n组物品
        int s;
        cin >> s;//读入每组物品的件数
        for(int k=1;k<=s;k++) cin >> v[k] >> w[k];//根据件数读入每组物品的每一件的体积和价值,会对上一层进行覆盖
        for(int j=m;j>=0;j--){//滚动数组倒序01背包
            for(int k=0;k<=s;k++){//枚举一组中的每一件物品(包括不选)
                if(j>=v[k]) f[j]=max(f[j],f[j-v[k]]+w[k]);
            }
        }
    }
    cout << f[m];
    return 0;
}

你可能感兴趣的:(动态规划,算法)