简单的DP(蓝桥)

简单的DP

1.01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

思路:

二维数组+动规
状态转移方程:
定义f(i,j):前i个物品,背包容量j下的最优解

1)当前背包容量不够(j < v[i]),为前i-1个物品最优解:f(i,j)=f(i-1,j)

2)当前背包容量够,判断选与不选第i个物品 选:f(i,j) = f(i-1,j-v[i]) + w[i]
不选:f(i,j)=f(i-1,j)

代码:

//无优化版
#include <iostream>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

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 = 0; j <= m; j++) {
            f[i][j] = f[i-1][j];
            if(j>=v[i]) 
               f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);
        }
    }

    	cout << f[n][m] << endl;
 return 0;    
}


//优化成一维
/*
1. f[i] 仅用到了f[i-1]层, 
2. j与j-v[i] 均小于j
3.若用到上一层的状态时,从大到小枚举, 反之从小到大哦
*/
#include <iostream>
#include <algorithm>

using namespace std;
const int N=1010;
int f[N];

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

2.摘花生

在这里插入图片描述

题意:

一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。

只能向东或向南走,不能向西或向北走。

问最多能够摘到多少颗花生。

思路:

每一步都与上一步有关,上一步可以从上面下来,也可以是从左边过来,我们只需要求这俩种情况下的最大值就是该状态下能摘的最大花生数。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N=110;
int f[N][N];
int w[N][N];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        memset(f,0,sizeof f);
        int r,c;
        cin>>r>>c;
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
            {
                cin>>w[i][j];
            }
            
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
            {
                f[i][j]=max(f[i-1][j],f[i][j-1])+w[i][j];
            }
            
        cout<<f[r][c]<<endl;
        
    }
    
    return 0;
}

3.最长上升子序列

题意:

给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。

思路:

每一个位置的状态都可以由前面的状态转换来,每个点的最长上升子序列最少是1,从前面开始遍历,如果前面的数比该数小,就可以取前面状态+1和当前状态的最大值。最后再从所有状态里面找一个最大的。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=1010;
int f[N],w[N];
//f[i]:前i个数的最长上升子序列

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>w[i];
    
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
        {
            if(w[j]<w[i])
                f[i]=max(f[i],f[j]+1);
        }
    }
    
    int res=0;
    for(int i=1;i<=n;i++) res=max(res,f[i]);
    
    cout<<res<<endl;
    
    return 0;
}
发布了75 篇原创文章 · 获赞 8 · 访问量 1259

猜你喜欢

转载自blog.csdn.net/qq_40905284/article/details/104893374