动态规划刷题练习

题型介绍

较常见的动态规划有三种题型:

可选的物品数量有限:01背包,多重背包

可选的物品数量无限:完全背包

注意!对于背包容量/预算金额这样的元素

如果是01背包和多重背包,背包容量要从前往后遍历

如果是完全背包,背包容量要从后往前遍历

还会有背包问题的变式,需要具体问题具体分析

至于其他诸多类型的dp,很多跟背包问题类似或者能利用背包问题的思维来解决

做题技巧

分为以下几步

  1. 定义状态:首先需要明确问题的子问题和状态。确定哪些变量需要被保存的状态(需要被加到dp里的,例如dp[i][j]里的i和j)并给出相应的定义(自己要知道这些变量的含义)。这些状态通常与问题的解相关。

        例如:dp[i][j]代表答案,i和j代表变量,要递推每一个i和j能产生的组合(相当于是一个填表格的过程)。

  1. 初始化:初始化已知的状态,使得问题的初始情况下能够求解。

  2. 状态转移方程:根据问题的定义和已知的状态,推导出状态之间的关系,即状态转移方程。这个方程描述了问题的递推关系,用于计算当前状态的值。

  3. 确定计算顺序:根据状态转移方程,确定计算的顺序。有些状态可能需要依赖于其他状态的值,因此需要保证这些依赖的状态已经计算过。

  4. 计算最终结果:根据计算顺序,将所有状态按照状态转移方程逐步计算出来,直到计算得到最终的解。

  5. 返回结果:根据问题的要求,返回计算得到的最终结果。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值