1、洛谷 台阶问题
传送门:台阶问题
最基础的台阶问题是一次可以走一层或者两层台阶 , 而这题不同的是一次可以走1到n层台阶。
那么该怎么做呢,首先我们还是从基础台阶问题来分析在基础台阶中我们假设一个dp数组dp[i] 是走到第i个台阶有几种走法那么他的走法是dp[i-1] + dp[i-2[所以就是dp[i] = dp[i-1] + dp[i-2];
因为有两种方案数。如果有三种呢,就是dp[i-1] +dp[i-2] + dp[i-3],k种的话就是dp[i-1] + ...+dp[i-k](i -k > 0)所以我们的状态转移方程就得到了。
下面是ac代码:
#include <bits/stdc++.h>
#define mod 100003
using namespace std;
using ll =long long;
const int N =1e6;
ll dp[N];
int n,k;
int main(){
cin>>n>>k;
dp[0] = 1;
dp[1] = 1;
for(int i =2;i<=n;++i){
for(int j =max(0,i-k);j<=i-1;++j){
dp[i] = (dp[i] +dp[j] )%mod;
}
}
cout<<dp[n];
return 0;
}
2、洛谷 数列分段
传送门:https://www.luogu.com.cn/problem/P1181
这题也是非常简单了,我们之前用一个sum就行了
#include <bits/stdc++.h>
using namespace std;
const int N =100009;
int a[N];
int n,m;
int main(){
cin>>n>>m;
for(int i =1;i<=n;++i)cin>>a[i];
int sum =0;
int ans =1;
for(int i =1;i<=n;++i){
if(sum +a[i]<= m)sum +=a[i];
else sum= a[i] , ans++;
}
cout<<ans;
return 0;
}
3、牛客 数组段数
传送门:点这里
本道题唯一的难点就是范围 ,范围比较大要用前缀和数组来优化
for(int i =1;i<=n;++i){
cin>>a[i];
b[i] = b[i-1] + (a[i] != a[i-1]);
}
这段代码就是优化,什么意思呢? 如果这个数和前面的数不一样的话,隔板就加一个,比如
1 2 2 3 , 这里1和前面那个数字(但其实前面没有数字,但是数组b[1]的前面是b[0])就有一个隔板 , 然后1和2之间也有一个隔板 , 2和3 之间又有一个隔板。所以有三个隔板,那么隔板的位置在哪里呢?这是我们需要考虑的,如果我们把1 2 2 3编号成 1 2 3 4 ,四个数字那么位置其实在第 1号 ,第2号 ,第四号 。然后就是如果两个数之间没有隔板的话, 本来就是有一段的,所以最后答案是要加1的。那么如果说询问区间 2 和 4 ,我们知道应该是有两个隔板的,如果按照正常的前缀和的话就是 b[4] - b[2-1] = 3 - 1=2,2+1 = 3了,与答案不符合 , 所以不能包括 上区间的左端点 ,应该是b[4] - b[2];
一下就是ac代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N], b[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i =1;i<=n;++i){
cin>>a[i];
b[i] = b[i-1] + (a[i] != a[i-1]);
}
while(m--){
int l,r;
cin>>l>>r;
cout<<b[r] - b[l] +1<<'\n';
}
return 0;
}
4、一系列背包问题的总结与模板
一、01背包
这个就是非常经典的问题了,昨天的文章中还写了变形的01背包问题,那么我们还是来介绍一下01背包这个问题吧。
一个包的体积为V , 有n个物品 每个物品的价值为wi , 体积为vi,而且每个物品只能使用一次,问能装价值最多是多少? 我们采用优化的 一维数组来解决这个问题
for(int i =1;i<=n;++i) //遍历物品
for(int j =v;j>=v[i];--j) //倒着遍历背包
dp[j] = max(dp[j] , dp[j - v[i]] + w[i] )
//v[i]表示单个物品的体积 w[i]表示单个物品的价值
那么这种最基础的01背包也是非常简单的,一定要掌握
二、完全背包
完全背包和01背包唯一的不同点就是每个物品能无限次的选取,此时我们只需要正的遍历背包即可
for(int i =1;i<=n;++i)
for(int j =v[i];j<=V;++j)
dp[j] = max(dp[j] , dp[j - v[i]] +w[i]);
三、多重背包
这个就是每个物品可以选择s次,但是这样的话时间复杂度会很高。我们可以采用优先队列或者二进制优化,这里主要说一下二进制优化的办法。
我们可以把s分成1,2,4,....,x几部分 ,比如8是1,2,4,1.分成这几个部分。
for(int i =1;i<=n;++i)
int v,w,s;
for(int k = 1;k<=s;s -= k,k +=k){
for(int j =m;j>=k*v;--j)dp[j] = max(dp[j] ,dp[j -v*k]+w*k);
}
for(int j =m;j>=s*v;--j)dp[j] = max(dp[j] ,dp[j -v*s]+w*s);
四、二维费用背包
这类问题是既有体积,又有重量,多了一个参数,但是大体不变
for(int i =1;i<=n;++i){
int v,m,w;cin>>v>>m>>w;
for(int j = M;j>=m;--j){
for(int k =V;k>=v;--k){
dp[j][k] = max(dp[j][k] , dp[j-m][k-v] + w);
}
}
}
五、分组背包
这个问题呢就是物品分成了几组比如
a b b c
a c d
c d
这些物品被分成了三组,每组只能选一个物品,或者可以不选
for(int i =1;i<=n;++i){
for(int j=1;j<=V;++j)dp[i][j] = dp[i-1][j];
int s;cin>>s;
while(s--){
int v,w;cin>>v>>w;
for(int k =V;k>=v;--k){
dp[i][k] = max(dp[i][k] , dp[i-1][k-v]+w)
}
}
}