梳理一下最近准备蓝桥杯时学习DP问题的想法

学习时间不长,记录的只是学习过程的思路和想法,不能保证正确,代码可以在acwing上AC。

01背包问题:

1.首先是简单的01背包问题

2.先确定状态,f[i][j]表示有第i件物品,时间为j的最大价值。

3.第i件物品取时f[i][j]=f[i-1][j-W[i]]+V[i],第i件物品不取时f[i][j]=f[i-1][j],物品只有取与不取两种情况,所以两个值取一个最大值即可

4.保证不重不漏的考虑了所有情况,f[n][m]就是答案。

4.放一个二维的简单写法,以及一维的优化写法。

 


#include<stdio.h> #define X 1010 int f[X][X]; int V[X],W[X]; int max(int a,int b) { return a>b?a:b; } int main(void) { int n,v; scanf("%d%d",&n,&v); for(int i = 1; i <= n ; i++)scanf("%d%d",&V[i],&W[i]); for(int i = 1; i <= n ; i++) for(int j = v ; j >=0 ; 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]); } printf("%d",f[n][v]); return 0 ; }

  


#include<stdio.h> #define X 1010 int f[X]; int res; int max(int a,int b) { return a>b?a:b; } int main(void) { int n,m,v,w; scanf("%d %d",&n,&m); for(int i = 1 ; i <= n ; i++){ scanf("%d %d",&v,&w); for(int j = m ; j >= v ; j--){ //从大到小逆序更新f[j],防止利用第i轮的较小数据更新第i轮的较大数据 //保证每一轮较大f[j]更新时使用的是第i-1轮的较小f[j] f[j]=max(f[j],f[j-v]+w); if(res<f[j])res=f[j]; } } printf("%d",f[m]); return 0; }

  

摘花生

1.还是一道很基础的DP问题

2.依旧先确定状态,f[i][j]表示第i行第j列时花生的数量。

3.只能向下走或者向右走两种情况,所以取向下走到f[i][j]和向右走到f[i][j]的最大值加上f[i][j]处的花生。

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

4.不断更新到f[r][c]就是答案。

5.水平有限只会写简单写法,可以去acwing上看大佬写的滚动数组优化和一维的优化写法。有机会学习完回来更新下优化写法。

 

#include<stdio.h>
#define X 110
int a[X][X];
int max (int  a, int b )
{
	return a > b ? a : b;
}
int main(void)
{
	int n;
	scanf("%d",&n);
	while(n--)
	{
		int c,r;
		scanf("%d %d",&r,&c);
		for(int i = 1 ; i <= r ; i++){
			for(int j = 1 ; j <= c ; j++){
				scanf("%d",&a[i][j]);
				a[i][j]+=max(a[i-1][j],a[i][j-1]);
			}
		}
		printf("%d\n",a[r][c]);
	}
	return 0;
 } 

 

  

 

最长上升子序列:

1.不难的一道基础题。

2.先确定状态,f[i]表示以a[i]结尾的最长子序列长度。只要a[j]<a[i],那么f[i]就应该等于f[j]+1,与f[i]两者的最大值;

3.更新每一个f[i]的值,最大值就是最长子序列长度。

4.可以去acwing上看大佬写的动规加二分做法,时间复杂度可以达到O(nlogn)。

 

#include<stdio.h>
#define X 1010
int a[X],f[X],res;
int max(int a,int b)
{
    return a>b?a:b;
}
int main(void)
{
    int n;
    scanf("%d",&n);
    for(int i = 0 ; i < n ; i++){
        scanf("%d",&a[i]);
    }
    for(int i = 0; i < n ;i ++){
       f[i]=1;
        for(int j = 0; j < i ; j++){
            if(a[i]>a[j])f[i]=max(f[i],f[j]+1);
            if(f[i]>res)res=f[i];
        }
    }
    printf("%d",res);
    
    return 0;
}

 

  

 

波动数列:

1.蓝桥杯原题,对现在的我来说有点难,看完讲解一步步做的。

2.还是先确定状态,方程f[i][j]表示以加到第i项的和模n余数为j的个数。

3.关键点在于推出s和n-1项的数列和模n同余。

4.f[i][j]=(f[i-1][(j-i*a)%n]+f[i-1][(j+i*b)%n])%MOD。

5.类似背包的问题的组合,但是有点抽象,将每种可能需要状态推出,下一层利用上一层存下的状态接着推出结果。

6.利用3中的结论答案就是f[n-1][s%n]。

7。注意每次取余需要用(a%b+b)%b这个公式转换为正余数。

 

#include<stdio.h>
#define MOD  100000007 
#define X 1010
int get_mod(int a,int b)
{
    return (a%b+b)%b;
}
int f[X][X];
int main(void)
{
    int n,s,a,b;
    scanf("%d%d%d%d",&n,&s,&a,&b);
    f[0][0]=1;
    for(int i = 1 ; i < n ; i++)
        for(int j = 0 ; j < n ; j++)
            f[i][j]=(f[i-1][get_mod(j-i*a,n)]+f[i-1][get_mod(j+i*b,n)])%MOD;
    printf("%d",f[n-1][get_mod(s,n)]);
    return 0;
}

 

  

 

地宫取宝:

1.蓝桥杯原题,水平有限,还是看完讲解后一步步做的。

2.对于DP问题的如何初始化正确的初始化,还是有点疑惑,还是题目写的少了。

3.还是确定状态f[i][j][u][v]表示位于i,j,手中的物品有u件,手中物品的最大价值为v。

4.有点类似背包问题,同样需要分成拿与不拿的两种情况。

5.不拿的情况等于向右走到i,j和向左走到i,j两种情况的方案和,拿的情况应该是上一轮所以可能可以取的情况的方案和,需要遍历求和。

6.最后答案就是在i,j位置取了k件的所有方案和,因为数字很大可能会爆int,所有每进行一次两个数相加就要取模一次。

7.最开始取零件最大价值为0只有一种合法方案,取一件的合法方案也只有一种,所以将两种情况初始化为1。 

#include<stdio.h>
#define X 51
#define MOD  1000000007
#define K 13
#define C 14
int f[X][X][K][C];//在i,j处时有u件物品,此时的最大价值是v
int W[X][X];
int main(void)
{
    int n,m,k;
    scanf("%d %d %d",&n,&m,&k);
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j <= m ; j ++)
            scanf("%d",&W[i][j]),W[i][j]++;
    //最开始取了
    f[1][1][1][W[1][1]]=1;
    //最开始不取
    f[1][1][0][0]=1;
    for(int i = 1 ; i <= n ; i ++ ){
        for(int j = 1 ; j <= m ; j++){
            for(int u = 0 ; u <= 12 ; u++){
                for(int v = 0 ; v <= 13 ; v++){
                    int &val = f[i][j][u][v];
                    //不拿
                    val = (val + f[i-1][j][u][v]) % MOD;
                    val = (val + f[i][j-1][u][v]) % MOD;
                    //拿
                        if(u>0&&v==W[i][j]){
                            //加上上一轮的所有可能可以拿的情况
                           for(int c = 0 ; c < v ; c++){
                            val=(val+f[i-1][j][u-1][c])%MOD;
                            val=(val+f[i][j-1][u-1][c])%MOD;
                           }
                    }
                }
            }
        }
    }
    int res=0;
    for(int i = 0 ; i <= 13 ;i++)res=(res+f[n][m][k][i])%MOD;
    printf("%d",res);
    
    return 0;
}

 

  

 

posted @ 2021-03-31 13:47  别问了我什么都不会  阅读(215)  评论(0)    收藏  举报