题型介绍
较常见的动态规划有三种题型:
可选的物品数量有限:01背包,多重背包
可选的物品数量无限:完全背包
注意!对于背包容量/预算金额这样的元素
如果是01背包和多重背包,背包容量要从前往后遍历
如果是完全背包,背包容量要从后往前遍历
还会有背包问题的变式,需要具体问题具体分析
至于其他诸多类型的dp,很多跟背包问题类似或者能利用背包问题的思维来解决
做题技巧
分为以下几步
-
定义状态:首先需要明确问题的子问题和状态。确定哪些变量是需要被保存的状态(需要被加到dp里的,例如dp[i][j]里的i和j),并给出相应的定义(自己要知道这些变量的含义)。这些状态通常与问题的解相关。
例如:dp[i][j]代表答案,i和j代表变量,要递推每一个i和j能产生的组合(相当于是一个填表格的过程)。
-
初始化:初始化已知的状态,使得问题的初始情况下能够求解。
-
状态转移方程:根据问题的定义和已知的状态,推导出状态之间的关系,即状态转移方程。这个方程描述了问题的递推关系,用于计算当前状态的值。
-
确定计算顺序:根据状态转移方程,确定计算的顺序。有些状态可能需要依赖于其他状态的值,因此需要保证这些依赖的状态已经计算过。
-
计算最终结果:根据计算顺序,将所有状态按照状态转移方程逐步计算出来,直到计算得到最终的解。
-
返回结果:根据问题的要求,返回计算得到的最终结果。
01背包问题
1.Bone Collector
本题介绍了二维压缩成一维
骨头数,袋子体积
骨头价值
骨头体积
求能带走最大价值是多少
分析:设一个dp[i][j],i代表若有i个骨头,j代表若有j大小的空间。
跟一般的一维dp不同,比如说求的是“第n天最多有多少只兔子”,那么i就代表第i天,dp[i]代表第i天最多的兔子。我们输入的只有一个变量——天数
但是这题不一样,我们输入的有两个属性,骨头数和袋子体积。因此要用二维dp记录这两个变量各种变化的时候所代表的最大价值。
设一个dp[i][j],i代表若有i个骨头,j代表若有j大小的空间。dp[i][j]代表当骨头有i个,袋子体积为j时能带走的最大价值
步骤之一,定义状态:本题属性 -> 骨头数,袋子体积
#include<queue>
#include<math.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
int dp[1010][1010];//前i个物品,目前背包容量
struct Bone {
int val, v;
}bone[1010];
signed main()
{
int T;
cin >> T;
while (T--) {
int num, bag;
cin >> num >> bag;
for (int i = 1; i <= num; i++) {
cin >> bone[i].val;
}
for (int i = 1; i <= num; i++) {
cin >> bone[i].v;
}
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= num; i++) {
for (int j = 0; j <= bag; j++) {
//骨头体积有可能为0,很坑。所以背包容量为0时也有可能装骨头
if (bone[i].v > j) {
//当前骨头体积肯定装不下,没有选择余地
dp[i][j] = dp[i - 1][j];
}
else {
//可以选择要不要装当前这块骨头
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - bone[i].v] + bone[i].val);
}
}
}
cout << dp[num][bag] << endl;
}
return 0;
}
二维dp可以压缩成一维dp。
这是二维dp的表格(虽然不是这题的)。
再看二维dp公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - bone[i].v] + bone[i].val);
假设i是行,j是列。发现只用到了第i-1行,而1---(i-1)行已经没用了,到后面也没用,所以可以一直覆盖来压缩成一维。
公式:dp[j] = max (dp[j] , dp[j-c[i].v] + c[i].w);
|
这个dp[j]就相当于dp[i-1][j]
压缩成一维
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[1111];
struct node
{
int v,w;
}c[1111];
int main()
{
int u;
int n,v;
scanf ("%d",&u);
while (u--)
{
scanf ("%d %d",&n,&v);
memset (dp,0,sizeof (dp));
for (int i = 1 ; i <= n ; i++) //读题要注意,这俩别反了
scanf ("%d",&c[i].w);
for (int i = 1 ; i <= n ; i++)
scanf ("%d",&c[i].v);
for (int i = 1 ; i <= n ; i++)
{
for (int j = v ; j >= c[i].v ; j--)
{
dp[j] = max (dp[j] , dp[j-c[i].v] + c[i].w);
}
}
printf ("%d\n",dp[v]);
}
return 0;
}
2.ACM排名
每题只能做一次,相当于每件物品只有一个,就是01背包嘛
#include <vector>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//#define int long long
#include<math.h>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 1e6 + 10;
int a[N];
int dp[N];//预算
struct Node {
int score, cost;
}node[10005];
int main()
{
int t, n;
cin >> t >> n;
for (int i = 1; i <= n; i++) {
cin >> node[i].score >> node[i].cost;
}
for (int i = 1; i <= n; i++) {
//完全背包问题(无限个),是从前往后遍历
//01背包和多重背包都是有限个,是从后往前遍历
for (int j = node[i].cost; j <= t; j++) {//时间
dp[j] = max(dp[j], dp[j - node[i].cost] + node[i].score);
}
}
cout << dp[t];
return 0;
}
3.打包
样例输入 Copy
6 5 4 10 2 2 20 3 2 40 4 3 30 3 3
样例输出 Copy
50
二维费用背包模板题
#include<math.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node {
int score, w, v;
}node[1000];
int dp[1000][1000];//体积,重量
signed main()
{
int V, W;
cin >> W >> V;
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
int a, b, c;
cin >> a >> b >> c;
node[i] = { a,b,c };
}
//01背包,每个物品只有一件,从后往前遍历
for (int i = 1; i <= n; i++) {//物品
for (int j = V; j >= node[i].v; j--) {//体积
for (int k = W; k >= node[i].w; k--) {//重量
dp[j][k] = max(dp[j][k], dp[j - node[i].v][k - node[i].w] + node[i].score);
}
}
}
cout << dp[V][W];
return 0;
}
4.分组问题
样例输入 Copy
10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3
样例输出 Copy
20
01背包+物品分组,这题是01背包的变式了
那么如何实现每组只取一件物品?
要把代码 j 的循环写在最里层,这样就是从一组里面选一个最优解
如果把 j 的循环写在第二层,k 的循环写在最里层(我原来就是这么写错的),那就跟普通的01背包没区别了,因为遍历了每一个物品,又没有去重(一组里只选一个)的操作
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node {
vector<int> w;//重量
vector<int> v;//价值
}node[1000];
int dp[300];//容量
signed main()
{
int bag, n, t;
cin >> bag >> n >> t;
for (int i = 1; i <= n; i++) {
int w, v, num;
cin >> w >> v >> num;
node[num].w.push_back(w);
node[num].v.push_back(v);
}
//多重背包+01背包,有限个数,容量从后往前
for (int i = 1; i <= t; i++) {//组
for (int k = bag; k >= 0; k--) {
for (int j = 0; j < node[i].w.size(); j++) {//遍历组内的操作必须在内层
//一组里只能选一个
int weight = node[i].w[j];
int val = node[i].v[j];
if (weight <= k) {
dp[k] = max(dp[k], dp[k - weight] + val);
}
}
}
}
cout << dp[bag];
return 0;
}
5.潜水员
样例输入 Copy
5 5 9 1 1 12 3 1 52 1 3 71 2 1 33 3 2 86 2 3 91 2 2 43 3 3 113 1 2 28
样例输出 Copy
104
也是一个二维费用背包问题,但不同于其他题,这题没有类似金额限制,O2和N2只要能满足需求就行,多出来没关系。
依然是选和不选的问题,但选的情况要注意可能j - node[i].o2或l - node[i].n2会小于0
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
//#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node {
int o2, n2, m;
//氧气,氮气,重量
}node[1005];
int dp[100][100];//氧气,氮气
int main()
{
memset(dp, 127, sizeof(dp));
int o2, n2;
cin >> o2 >> n2;
int K;
cin >> K;
for (int i = 1; i <= K; i++) {
cin >> node[i].o2 >> node[i].n2 >> node[i].m;
}
dp[0][0] = 0;//可以先想一下有没有需要先初始化的
for (int i = 1; i <= K; i++) {
//涉及容量/金额等元素,01背包要倒着遍历
for (int j = o2; j >= 0; j--) {
for (int l = n2; l >= 0; l--) {
dp[j][l] = min(dp[j][l], dp[max(j - node[i].o2, 0)][max(l - node[i].n2, 0)] + node[i].m);
}
}
}
cout << dp[o2][n2];
return 0;
}
这种做法思路跟上面不同的是,
这个是先选了,然后考虑选还是不选
上面那个是还没选,考虑选还是不选
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//#define int long long
#include<math.h>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 1e6 + 10;
struct Node {
int o2, n2, m;
//氧气,氮气,重量
}node[1005];
int dp[100][100];//氧气,氮气
int main()
{
memset(dp, 127, sizeof(dp));
int o2, n2;
cin >> o2 >> n2;
int K;
cin >> K;
for (int i = 1; i <= K; i++) {
cin >> node[i].o2 >> node[i].n2 >> node[i].m;
}
dp[0][0] = 0;//可以先想一下有没有需要先初始化的
for (int i = 1; i <= K; i++) {
//涉及容量/金额等元素,01背包要倒着遍历
for (int j = o2; j >= 0; j--) {
for (int l = n2; l >= 0; l--) {
int t1 = j + node[i].o2;
int t2 = l + node[i].n2;
//超过最大需求,就当作最大需求的判断
//这里的特判跟上面的题解中,特判有没有小于0的作用是一样的
if (t1 > o2) t1 = o2;
if (t2 > n2) t2 = n2;
dp[t1][t2] = min(dp[i1][t2] , dp[j][l] + node[i].m) {
}
}
}
cout << dp[o2][n2];
return 0;
}
多重背包问题
1.庆功会
#include <vector>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//#define int long long
#include<math.h>
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
struct Node {
int price, val, num;
//价格,价值,可购买数量
}node[7000];
int dp[7000];//预算,购买数量
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> node[i].price >> node[i].val >> node[i].num;
}
for (int i = 1; i <= n; i++) {//物品
for (int j = m; j >= node[i].price; j--) {//一定要从后往前
for (int k = 0; k <= node[i].num && k * node[i].price <= j; k++) {
//第i件物品可购买的数量
//预算
dp[j] = max(dp[j], dp[j - k * node[i].price] + k * node[i].val);
}
}
}
cout << dp[m];
return 0;
}
完全背包问题
1.完全背包问题
模板题
样例输入 Copy
10 4 2 1 3 3 4 5 7 9
样例输出 Copy
max=12
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
struct Node {
int w, v;//重量,价值
}node[40];
int dp[205];//背包容量
int main()
{
int bag, n;
cin >> bag >> n;
for (int i = 0; i < n; i++) {
cin >> node[i].w >> node[i].v;
}
//dp[0] = 0;
for (int i = 0; i < n; i++) {//物品
for (int j = node[i].w; j <= bag; j++) {//容量
dp[j] = max(dp[j - node[i].w] + node[i].v, dp[j]);
}
}
cout << "max=" << dp[bag];
return 0;
}
2.混合背包
样例输入 Copy
10 3 2 1 0 3 3 1 4 5 4
样例输出 Copy
11
如果单拎出来这题可能会有点发懵,但是这题跟上一题一模一样的,只是把完全背包的物品数量变成inf就行,其他的不用管。因为如果数量太多了,价格支付不起,自己会跳出循环
#include<math.h>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 1e6 + 10;
struct Node {
int price, val, num;
//重量,价值,可购买数量
}node[7000];
int dp[7000];//预算,购买数量
int main()
{
int n, m;
cin >> m >> n;
for (int i = 1; i <= n; i++) {
cin >> node[i].price >> node[i].val >> node[i].num;
if (node[i].num == 0) node[i].num = inf;
}
for (int i = 1; i <= n; i++) {//物品
for (int j = m; j >= node[i].price; j--) {//一定要从后往前
for (int k = 0; k <= node[i].num && k * node[i].price <= j; k++) {
//第i件物品可购买的数量
//预算
dp[j] = max(dp[j], dp[j - k * node[i].price] + k * node[i].val);
}
}
}
cout << dp[m];
return 0;
}
3. Coin Change
思路跟Bone Collector一模一样,有两个属性,二维dp
但这题是完全背包问题,即可选的硬币有无限个
二维写法
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
//只用前i(5)个硬币,金额(250)
int dp[1000][1000];//1,5,10,25,50
int coin[6] = { 0,1,5,10,25,50 };
signed main()
{
int n;
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= 5; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 251; j++) {
if (coin[i] > j)
{
dp[i][j] = dp[i - 1][j];
}
else {
dp[i][j] += dp[i - 1][j];
dp[i][j] += dp[i][j - coin[i]];
}
}
}
while (cin >> n) {
cout << dp[5][n] << endl;
}
return 0;
}
一维写法。
注意!这题跟Bone Collector不一样,这题求的是最大方案数, 因此选和不选的方案要加起来然后放到dp[j]
//前i(5)个硬币,金额(250)
int dp[1000];//1,5,10,25,50
int coin[6] = { 0,1,5,10,25,50 };
signed main()
{
int n;
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 251; j++) {
//如果写成for (int j = coin[i]; j <= 251; j++),这个if的判断就可以省略了
if (coin[i] > j)
{
continue;//不操作,dp[j]没变,也就是dp[i-1][j]
}
else {
//有得选择
dp[j] = dp[j] + dp[j - coin[i]];
//注意,题目求的是方案数,所以当前最大方案数 = 选的方案数+不选的方案数
}
}
}
while (cin >> n) {
cout << "答案:" << dp[n] << endl;
}
return 0;
}
但如果加上个限制条件,选的硬币不能超过100枚,总共有三个限制条件,就一定要用上面的一维dp进行优化,再加个变量,即已选择的硬币数k,然后变成二维dp。
其本质上是三维dp!!!
变成了多重背包问题!
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
int coin[5] = { 1,5,10,25,50 }/*下标从零开始,前面加个0*/;
int dp[110][260];//最多用上k个硬币,金额j
signed main() {
int n;
while (cin >> n) {
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 0; i < 5; i++) {//物种货币
for (int k = 1; k <= 100; k++) {
for (int v = coin[i]; v <= n; v++) {
dp[k][v] += dp[k - 1][v - coin[i]];
}
}
}
int ans = 0;
for (int k = 0; k <= 100; k++) {//切记!n可能为0,一枚硬币都不用选!所以k=0不能漏
ans += dp[k][n];
}
cout << ans << endl;
}
return 0;
}
4.开心的天宝
样例输入 Copy
1000 5 800 2 400 5 300 5 400 3 200 2
样例输出 Copy
3900
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
const int N = 1e6 + 10;
int a[N];
int dp[N];//预算
struct Node {
int price, val;
}node[30];
int main()
{
int m;
cin >> m;
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> node[i].price >> node[i].val;
}
for (int i = 1; i <= n; i++) {//物品
for (int j = m; j >= node[i].price; j--) {
dp[j] = max(dp[j], dp[j - node[i].price] + node[i].price * node[i].val);
}
}
cout << dp[m];
return 0;
}
5.货币系统
样例输入 Copy
3 10 1 2 5
样例输出 Copy
10
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e6 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[N];
int dp[N];//金额
signed main()
{
int m, n;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
dp[0] = 1;//注意初始化
//无限,是完全背包问题
for (int i = 1; i <= n; i++) {//货币
for (int j = a[i]; j <= m; j++) {
dp[j] += dp[j - a[i]];//这题求的是方案数,选和不选的都加上
}
}
cout << dp[m];
return 0;
}
其他dp练习
1.Basketball Exercise
Examples
输入
5 9 3 5 7 3 5 8 1 4 5
输出
29
输入
3 1 2 9 10 1 1
输出
19
输入
1 7 4
输出
7
#include<stdlib.h>
#include<string>
#include<string.h>
#include <stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<sstream>
#include<stack>
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e5 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[100005][3];//列。一个都不选,选上面的,选下面的
int a[N], b[N];//第一排,第二排
signed main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
dp[1][0] = 0, dp[1][1] = a[1], dp[1][2] = b[1];
for (int i = 2; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][1], dp[i - 1][2]));
dp[i][1] = max(dp[i - 1][0], dp[i - 1][2] ) + a[i];
dp[i][2] = max(dp[i - 1][0], dp[i - 1][1] ) + b[i];
}
cout << max(dp[n][0], max(dp[n][1], dp[n][2]));
return 0;
}
2.Mocha and Red and Blue
输入 #1复制
5 7 ?R???BR 7 ???R??? 1 ? 1 B 10 ?R??RB??B?
输出 #1复制
BRRBRBR BRBRBRB B B BRRBRBBRBR
先思考一个问题,最大有几个相邻的位置字母不一样
#include <utility>
#include<map>
#include <vector>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define int long long
//#include<bits/stdc++.h>
const int N = 1e5 + 10;
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int dp[200][3];
//dp[i][0]代表第i个位置选B的最长ans
//dp[i][1]代表第i个位置选R的最长ans
//ans是最大有几个相邻的位置字母不一样
string s;
signed main()
{
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
cin >> s;
s = '.' + s;
memset(dp, 0, sizeof(dp));
for (int i = 2; i <= n; i++) {
//当前位置要选B的情况
if (s[i] == '?' || s[i] == 'B') {
//可以选B
dp[i][0] = max(dp[i - 1][1] + 1, dp[i - 1][0]);
}
else {
//这个位置是R,无法更改
dp[i][0] = 0;
}
//当前位置要选R的情况
if (s[i] == '?' || s[i] == 'R') {
//可以选R
dp[i][1] = max(dp[i - 1][0] + 1, dp[i - 1][1]);
}
else {
//这个位置是B,无法更改
dp[i][1] = 0;
}
}
cout << "答案:";
cout << max(dp[n][0], dp[n][1]) << endl;
}
return 0;
}
方法1:贪心
方法2:dp
3.Constanze's Machine
样例 #1
输入copy | 输出copy |
---|---|
ouuoharinn | 4 |
样例 #2
输入copy | 输出copy |
---|---|
banana | 1 |
样例 #3
输入copy | 输出copy |
---|---|
nnn | 3 |
样例 #4
输入copy | 输出copy |
---|---|
amanda | 0 |
挺无语的,以后看到稍微简单点的dp可以试着找找规律!!!
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int modulo=1e9+7;
string s;int n,u;long long ans,fib[100004];bool flag;
int main(void)
{
cin>>s;ans=1;flag=true;
fib[1]=1;fib[2]=1;
for (int i=3;i<=100002;i++)
{
fib[i]=(fib[i-1]+fib[i-2])%modulo;
}
for (int i=0;i<s.size();i++)
{
if (s[i]=='n')
{
n++;
if (u>1) flag=true,ans*=fib[u+1],ans%=modulo;
u=0;
}
else if (s[i]=='u')
{
u++;
if (n>1) flag=true,ans*=fib[n+1],ans%=modulo;
n=0;
}
else if (s[i]!='m'&&s[i]!='w')
{
if (n>1) flag=true,ans*=fib[n+1],ans%=modulo;
n=0;
if (u>1) flag=true,ans*=fib[u+1],ans%=modulo;
u=0;
}
else
{
cout<<0;
return 0;
}
}
if (u>1) flag=true,ans*=fib[u+1],ans%=modulo;
if (n>1) flag=true,ans*=fib[n+1],ans%=modulo;
if (flag==true) cout<<ans;
else cout<<0;
}
4.小美的01串翻转
先看个阉割版的
游游拿到了一个01串(仅由字符'0'和字符'1'构成的字符串)。游游每次操作可以选择对其中一个字符取反(即1变0,或者0变1),对第 i 个字符取反的代价为 1。(代价从1开始计算,即第一个字母的代价是1)
游游希望最终字符串任意两个相邻的字符都不相同,她想知道花费代价之和的最小值的多少?
int dp[100010][2];//到第i个。第i个选0/1
signed main()
{
string s;
cin >> s;
int n = s.size();
if (s[0] == '1') dp[0][0] = 1;
else dp[0][1] = 1;
for (int i = 1; i < n; i++) {
if (s[i] == '1') dp[i][0] = dp[i - 1][1] + 1, dp[i][1] = dp[i - 1][0];
else dp[i][1] = dp[i - 1][0] + 1, dp[i][0] = dp[i - 1][1];
}
cout << min(dp[n - 1][0], dp[n - 1][1]);
return 0;
}
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。
例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。
现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
输入描述:
一个仅包含'0'和'1'的字符串,长度不超过 2000。
输出描述:
所有非空子串的权值和。
示例1
输入
复制10001
10001
输出
复制8
8
说明
长度为 2 的子串中,有 2 个"00"的权值是 1。
长度为 3 的 3 个子串权值都是 1。
长度为 4 的 2 个子串权值都是 1。
长度为 5 的 1 个子串权值是 1。
总权值之和为 2+3+2+1=8