蓝桥杯:简单DP&闫氏思考法

前言:

蓝桥杯对DP的考察还是比较多的,引起重视。

蓝桥杯最常见的三种形式:选择问题/组合问题(eg:背包问题 在众多选法中选择一个最优的选法)、路线问题(规定规则,按照这个规则走,找出最优的一条路线)、线性问题(一维的,例如最长上升子序列

有时候单独出现,经常是这三种形式的组合问题

建议:多做题,用过这个状态表示,考试时想出来这种状态表示概率才比较大

经验:

1.状态表示:第一维选择前i个物品

第二维一般是各种限制,体积、重量、选几个......

属性:Max/Min/数量

2.状态计算:集合划分为若干个子集,依据是找最后一个不同点

集合划分原则:不重不漏

3.优化:DP问题的所有优化都是看能不能对代码进行等价变形,换而言之优化后和问题本身没有关系,只和代码逻辑有关系

先用朴素的方法分析,在此基础上看能不能优化

  • 选择问题

例题:  acWing2. 01背包问题

1.状态表示:

集合 所有只考虑前i个物品总体积不超过j的选法的集合

属性 Max

f[i][j]

2.状态计算:

(1)所有选择第i个物品的方案 f[i-1][j]

(2)所有不选择第i个物品的方案 f[i-1][ j-v[i] ]+w[i]

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

3.优化:

滚动数组

4.代码

(1)朴素写法

#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;
}

(2)优化写法

#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[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=m;j>=v[i];j++)
    {
      f[j]=max(f[j],f[j-v[i]]+w[i]);
    }
  }
  cout<<f[m]<<endl;
  return 0;
}
  • 路线问题

例题:acWing1015. 摘花生

1.状态表示:

集合  所有从(1,1)走到(i,j)的路线

属性  Max

f[i][j]

2.状态计算

(1)最后一步是从上面下来  f[i-1][j]+w[i][j]

(2)最后一步是从左边过来的  f[i][j-1]+w[i][j]

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

3.代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=110;
int n,m;
int w[N][N];
int f[N][N];
int main()
{
   int t;
   scanf("%d",&t);
   while(t--)
   {
       scanf("%d%d",&n,&m);
       for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
           scanf("%d",&w[i][j]);
       for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
             f[i][j]=max(f[i-1][j]+w[i][j],f[i][j-1]+w[i][j]);
       printf("%d\n",f[n][m]);
   }
   return 0;
}
  • 线性问题

例题:acWing 895. 最长上升子序列

1.状态表示:

集合  所有以a[i]结尾的严格单调上升子序列

属性  Max

f[i]

2.状态计算

最后一个不同点是倒数第二个数

(1)以a[1]结尾的上升子序列  f[1]+1

(2)以a[2]结尾的上升子序列  f[2]+1

...(i-1) 以a[i-1]结尾的上升子序列 f[i-1]+1

(i)空 1

f[i]=max( f[1]+1,f[2]+1,...,f[i-1]+1,1)

3.代码

#include<iostream>
using namespace std;
const int N=1010;
int n;
int a[N],f[N];
int main()
{
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  for(int i=1;i<=n;i++)
  {
    f[i]=1;
    for(int j=1;j<i;j++)
    {
      if(a[j]<a[i])
       f[i]=max(f[i],f[j]+1);
    }
  }
  int res=0;
  for(int i=1;i<=n;i++) res=max(res,f[i]);
  printf("%d\n",res);
  return 0;
}

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值