dp问题

01背包

#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[1001],w[1001];
int main()
{
    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-v[i]]+w[i],f[j]);
    }
    cout<<f[m];
}

完全背包

#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[1001],w[1001];
int main()
{
    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=v[i];j<=m;j++)
        f[j]=max(f[j-v[i]]+w[i],f[j]);
    }
    cout<<f[m];
/*
  f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
  f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
  因此 f[i][j]=f[i−1][j]+f[i][j−i];f[i][j]=f[i−1][j]+f[i][j−i]; (这一步类似完全背包的推导)
*/
}

多重背包(easy

#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[101],w[101],l[101];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i]>>l[i];
    
    for(int i=1;i<=n;i++)
        for (int k = 1; k <= l[i];k++)
            for (int j = m; j >= v[i]; j--)
                f[j] = max(f[j - v[i]] + w[i], f[j]);
    cout<<f[m];
}

多重背包(hard

#include<bits/stdc++.h>
using namespace std;
const int N=2001;
int n, m, f[N], v[N], w[N], l[N];

int qpow(int x,int k)
{
    int ans=1;
    while(k)
    {
        if(k&1) ans*=x;
        x*=x;
        k>>=1;
    }
    return ans;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        cin >> v[i] >> w[i] >> l[i];
    }
    for (int i = 1; i <= n;i++)
    {
        int res = l[i];
        for (int k = 1; k <= res; res -= k, k *= 2)
        {
            for (int j = m; j >= v[i] * k;j--)
            {
                f[j] = max(f[j], f[j - v[i] * k] + w[i] * k);
            }
            
        }
        for (int j = m; j >= v[i] * res;j--)
            {
                f[j] = max(f[j], f[j - v[i] * res] + w[i] * res);
            }
    }
    cout << f[m] << endl;
    return 0;
}

分组背包(优化版

#include<bits/stdc++.h>
using namespace std;
const int N=1001;
int n, m, f[N], v[N], w[N], l[N];
vector<int> idx[N];
int qpow(int x,int k)
{
    int ans=1;
    while(k)
    {
        if(k&1) ans*=x;
        x*=x;
        k>>=1;
    }
    return ans;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n;i++)
    {
        int id;
        cin >> id >> v[i] >> w[i];
        idx[id].push_back(i);
    }
    for (int i = 1; i <= 1000;i++)//一共有几类
    {
        for (int j = m; j ;j--)//背包容量
            for(auto k:idx[i])//每一类的物品
            {
                if(v[k]<=j)
                f[j] = max(f[j], f[j - v[k]] + w[k]);
            }

    }
    cout << f[m]<<endl;
    return 0;
}//就是把组看成物品总数 先看第几类 再看当前类的所有物品(前提是满足背包容量

二维背包(优化

#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n, m, k, f[N][N], v[N], w[N], l[N];

int qpow(int x,int k)
{
    int ans=1;
    while(k)
    {
        if(k&1) ans*=x;
        x*=x;
        k>>=1;
    }
    return ans;
}

int main()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n;i++)
        cin >> v[i] >> w[i] >> l[i];

    for (int i = 1; i <= n;i++)
    {
        for (int j = m; j ; j--)
        {
            for (int x = k; x ;x--)
            {
                    f[j][x] = f[j][x];
                if(x>=l[i]&&j>=v[i])
                    f[j][x] = max(f[j][x], f[j - v[i]][x - l[i]] + w[i]);
            }
        }
    }
    cout << f[m][k];
    return 0;
}

二维背包(朴素

#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n, m, k, f[N][N][N], v[N], w[N], l[N];

int qpow(int x,int k)
{
    int ans=1;
    while(k)
    {
        if(k&1) ans*=x;
        x*=x;
        k>>=1;
    }
    return ans;
}

int main()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n;i++)
        cin >> v[i] >> w[i] >> l[i];

    for (int i = 1; i <= n;i++)
    {
        for (int j = 0; j <= m; j++)
        {
            for (int x = 0; x <= k;x++)
            {
                    f[i][j][x] = f[i - 1][j][x];
                if(x>=l[i]&&j>=v[i])
                    f[i][j][x] = max(f[i][j][x], f[i - 1][j - v[i]][x - l[i]] + w[i]);
            }
        }
    }
    cout << f[n][m][k];
    return 0;
}


01背包边界是 最后一个物品取或不取:取得话 就是f[i-1][j-v[i]]+w[i] 不取就是f[i-1][j]; 滚动数组实现 因为是一维 所以先小的被更改 但是要用到原先的数据 所以从大到小枚举
完全背包边界是 最后一个物品取或不取 取得话 就是f[i][j-v[i]]+w[i](第i个物品是多个 所以可以继续取 第一维应该还是i) 不取就是f[i-1][j]; 也是滚动数组实现 因为是一维 但是 边界是用的第i个 所以从小到大枚举
多重背包easy 就是 把1类物品多个 看成多个一样的物品 比如 x个1 那就是 1 1 1 1...1 然后当作01背包来写
多重背包hard 就是 借助二进制分配(一个数字 肯定能用一堆2的次幂的数字来表示 比如 3=1+2 9=1+2+4+2


循环枚举顺序 先是物品种类数 再枚举单个物品的个数(可以直接暴力 或者二进制分解) 最后按照体积来枚举(要看前面的数量) 看是从大到小 还是从小到大

最长上升子序列

#include<bits/stdc++.h>
using namespace std;

const int N=1010;
int a[N],f[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
        
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j])
                f[i]=max(f[i],f[j]+1);
        }
    }
    
    int idx=-1;
    for(int i=1;i<=n;i++)
        idx=max(idx,f[i]);
        
    cout<<idx;
    return 0;//dp是在前面被计算的基础上继续做的 所以考虑转移方程的时候不用考虑之前的怎么算的 之前的就是通过现在的算出来的 递推来的
}

区间dp

所有的区间dp问题枚举时,第一维通常是枚举区间长度,并且一般 len = 1 时用来初始化,枚举从 len = 2 开始;第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1)

for (int len = 1; len <= n; len++) {         // 区间长度
    for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
        int j = i + len - 1;                 // 区间终点
        if (len == 1) {
            dp[i][j] = 初始值
            continue;
        }

        for (int k = i; k < j; k++) {        // 枚举分割点,构造状态转移方程
            dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
        }
    }
posted @ 2023-01-06 10:40  Szang  阅读(30)  评论(0编辑  收藏  举报