蓝桥杯备考(数学与简单DP)

例题 

买不到的数目

蓝桥杯备考(数学与简单DP)_第1张图片

 这道题目通过打表找规律,得到答案为(n-1)*(m-1)-1,然后直接输出答案即可,下面附上打表代码和ac代码(打表是找规律的一种好的方法)

性质:两个互质的数p和q,最大不能表示的数为(p-1)*(q-1)-1

打表代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
bool dfs(int x,int a,int b)//表示x是否可以由a和b表示 
{
    if(!x) return true;
    if(x>=a&&dfs(x-a,a,b)) return true;
    if(x>=b&&dfs(x-b,a,b)) return true;
    return false;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1000;i>=n;i--)
    {
        if(!dfs(i,n,m))
        {
            printf("%d",i);
            break;
        }
    }
    return 0;
}

ac代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    printf("%d",(n-1)*(m-1)-1);
    return 0;
}

蚂蚁感冒

蓝桥杯备考(数学与简单DP)_第2张图片

输入样例1:

3
5 -2 8

输出样例1:

1

输入样例2:

5
-10 8 -20 12 25

输出样例2:

3

 这道题需要发现一个特点,就是蚂蚁与蚂蚁之间其实是没什么区别的,也就是说两个蚂蚁碰头之后转身继续行走相当于直接穿过对方的身体,知道了这个性质之后这道题目就比较好想了,举一个简单的例子,假如一开始蚂蚁是向右走的,首先他初始位置右边且向左走的蚂蚁都会被传染,那他左边向右走的蚂蚁呢?这个时候就要看看是否存在他右边且向左走的蚂蚁了,如果有的话就会被传染,否则就不会,如果一开始蚂蚁是向左走的也是一样的分析方式

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=10003;
int a[N];
int main()
{
    int n,t,tt;
    cin>>n>>t;
    for(int i=2;i<=n;i++) scanf("%d",&a[i]);
    int ans=1;//记录感冒的蚂蚁数目 
    int cnt=0;//cnt记录与蚂蚁同方向且在蚂蚁后面的蚂蚁数量
    bool flag=false;//记录在第一只蚂蚁离开杆子之前能否碰到反向的蚂蚁 
    if(t<0)//蚂蚁一开始朝左走 
    {
        for(int i=2;i<=n;i++)
        { 
            if(a[i]>0&&a[i]<-t)//蚂蚁在第一只蚂蚁的左边且朝右走 
            {
                flag=true;
                ans++; 
            }
            if(a[i]<0&&a[i]t)//蚂蚁在第一只蚂蚁的右边且朝左走
            {
                flag=true;
                ans++;
            }
            if(a[i]>0&&a[i]

饮料换购

蓝桥杯备考(数学与简单DP)_第3张图片

 这个直接按照题意模拟就行,终止条件就是当前瓶盖数小于3.详情见代码

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
    int n,ans,r;
    cin>>n;
    ans=n;
    while(n>=3)
    {
        ans+=n/3;
        n=n%3+n/3;
    }
    printf("%d",ans);
    return 0;
}

背包问题

蓝桥杯备考(数学与简单DP)_第4张图片

 常见的背包问题我在之前博客种有过详细介绍,可以参考下之前博客常见背包问题_AC__dream的博客-CSDN博客

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1003;
int dp[N];
int main()
{
    int n,V;
    cin>>n>>V;
    int v,w;
    for(int i=1;i<=n;i++)
    {
        cin>>v>>w;
        for(int j=V;j>=v;j--)//此处要倒序遍历,保证遍历每一个dp数组时用到的是上一个dp数组的值,且上一个dp数组还未被更新 
            dp[j]=max(dp[j],dp[j-v]+w);
    }
    printf("%d",dp[V]);
    return 0;
}

 摘花生

蓝桥杯备考(数学与简单DP)_第5张图片

 蓝桥杯备考(数学与简单DP)_第6张图片

 这道题目是一个递推题,我们从左下到右上依次递推,走到当前格子所能获得的花生的最大数量等于当前格子的花生数量加上当前格子上面一个格子和左面一个格子的最大值,就这样最后输出终点坐标处所能获得的最大花生数量即可

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=103;
int s[N][N];
int main()
{
    int T,r,c;
    cin>>T;
    while(T--)
    {
        memset(s,0,sizeof s);
        scanf("%d%d",&r,&c);
        for(int i=1;i<=r;i++)
        for(int j=1;j<=c;j++)
        {
            scanf("%d",&s[i][j]);
            s[i][j]+=max(s[i-1][j],s[i][j-1]);
        }
        printf("%d\n",s[r][c]);
    }
    return 0;
}

最长上升子序列

蓝桥杯备考(数学与简单DP)_第7张图片

 最长子序列问题我在之前博客中有详细介绍,详情可以参考之前博客最长子序列问题_AC__dream的博客-CSDN博客d

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1003;
int a[N],ans[N];
int main()
{
    int n;
    cin>>n;
    memset(ans,0x3f,sizeof ans);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        *lower_bound(ans+1,ans+n+1,a[i])=a[i];
    }
    printf("%d",lower_bound(ans+1,ans+n+1,0x3f3f3f3f)-ans-1);
    return 0;
}

 习题

地宫取宝

蓝桥杯备考(数学与简单DP)_第8张图片

输入样例1:

2 2 2
1 2
2 1

输出样例1:

2

输入样例2:

2 3 2
1 2 3
2 1 5

输出样例2:

14

 这道题目我们主要用动态规划来解决,是前一道题的进阶版,就是在前一道题的基础上加了一些限制条件,dp[i][j][k][l]表示当前在(i,j)这个点已经选取了k个物品,k个物品中的最大值是l的方案数,这样我们就可以根据题意进行正常递推了,但是有一个坑就是宝贝的价值是从0开始的,由于我们每次选宝贝都要比之前选的宝贝价值大,而我们初始状态下默认已选择的宝贝的最大值为0,这样就导致我们在更新过程中无法选择价值为0的宝贝,所以我们一开始应将所有的宝贝价值加1,让宝贝的价值从1开始就可以了,细节见代码

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=53;
const int mod=1000000007;
long long w[N][N],dp[N][N][N][N];//dp[i][j][k][l]表示当前在(i,j)这个点已经选取了k个物品,k个物品中的最大值是l的方案数 
int main()
{
    long long n,m,k,mx=0;//mx记录宝贝的最大价值 
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        scanf("%lld",&w[i][j]);
        w[i][j]++;//让宝贝价值最小为1,因为初始我们没有选宝贝时默认当前已选宝贝最大值为0,这样就没有办法选择价值为0的宝贝 
        mx=max(mx,w[i][j]);
    }
    dp[1][1][1][w[1][1]]=dp[1][1][0][0]=1;//初始化
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        for(int p=0;p<=k;p++)
        for(int q=0;q<=mx;q++)
        {
            dp[i][j][p][q]=(dp[i][j][p][q]+dp[i-1][j][p][q]+dp[i][j-1][p][q])%mod;//不选(i,j)内的宝贝
            if(w[i][j]>q&&p>0) dp[i][j][p][w[i][j]]=(dp[i][j][p][w[i][j]]+dp[i][j-1][p-1][q]+dp[i-1][j][p-1][q])%mod;//选(i,j)内的宝贝 
        }
    }
    long long ans=0;
    for(int i=0;i<=mx;i++)
        ans=(ans+dp[n][m][k][i])%mod;
    printf("%lld",ans);
    return 0;
}

波动数列

蓝桥杯备考(数学与简单DP)_第9张图片

 s=n*x+(n-1)d1+(n-2)d2+…+d(n-1)
则 (n-1)d1+(n-2)d2+…+d(n-1)与s模n同余,那我们只需要枚举每次选择的公差并进行递推即可 
dp[i][j]记录只考虑前i个公差的选择,(n-1)d1+(n-2)d2+…+(n-i)di对n的余数 
则递推式即为 dp[i][j]+=dp[i-1][((j-t1)%n+n)%n],dp[i][j]+=dp[i-1][((j-t2)%n+n)%n],t1和t2是公差的倍数 

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1003;
const int mod=100000007; 
int dp[N][N];//dp[i][j]记录只考虑前i个公差的选择,前i项的和对n的余数 
int main()
{
    long long n,s,a,b;
    cin>>n>>s>>a>>b;
    while(s<0) s+=n;//小于0的s让其每次加n,相当于每次数列中每个数都加1,方案数不变 
    b*=-1;
    dp[0][0]=1;
    for(int i=1;i

你可能感兴趣的:(蓝桥杯备考,蓝桥杯)