蓝桥15届stema编程题密码锁-动态规划 C++和Python最后一道题

蓝桥2024年1月15届STEMAC++中级真题
第六题
编程实现:密码锁
提示信息:
密码锁:由 n 个从左到右并排的圆环组成,每个圆环上都有 10 个数字(0~9),蓝色框内为密码显示区,每个圆环在密码显示区只能显示一个数字,如图所示。可以拨动圆环,来改变密码显示区显示的数字。当密码显示区的数字与密码一致时,密码锁就会被打开。

题目描述:
有一个由 n 个圆环组成的密码锁,和一个 n 位的密码 S(S 由 1~9 中的数字(包含 1和 9)组成)。每次操作只能选择一个或位置连续的多个圆环拨动,当 S 中的字符从左
到右依次显示在密码显示区时,密码锁会被打开。已知每个圆环在密码显示区初始数字都为 0,请计算出最少需要操作多少次,才能打开密码锁。
注意:
1、如果选择了其中一个圆环,可将该圆环中任意一个数字拨动到密码显示区,表示 1 次操作;
例如:将第 3 个圆环拨动到数字 4,表示 1 次操作:

2、如果选择了位置连续的多个圆环,只能将这些圆环拨动成同一个数字,显示在密码显示区,表示 1 次操作。
例如:将连续的第 2 个到第 3 个圆环都拨动到数字 5,表示 1 次操作:

例如:
n = 5,S = "12321";分别表示 5 个圆环组成的密码锁和密码 12321;将 5 位密码 1、2、3、2、1 从左到右依次显示在密码显示区,以下是操作最少次数的方案:
第一次操作,将 5 个初始状态为 0 的圆环全部拨动到数字 1:

第二次操作,将第 2 个到第 4 个圆环全部拨动到数字 2:

第三次操作,将第 3 个圆环拨动到数字 3:

最少需要操作 3 次,才能打开密码锁。
输入描述:
第一行输入一个整数 n(1≤n≤100),表示组成的密码锁的圆环数及密码的位数
第二行输入一个长度为 n 的字符串 S,S 由 1~9 中的数字(包含 1 和 9)组成,表示密码

输出描述:
输出一个整数,表示最少需要操作多少次,才能打开密码锁
样例输入:
5
12321
样例输出:
3

这个解法是参考https://blog.csdn.net/qq_36230375/article/details/136615156的动态规划方案。
对于动态规划目前还是有些一知半解,继续加油!

#include <iostream>
#include <iomanip>
using namespace std;
/*这道题使用动态规划
要求的是从第一个数到第N个数的最小拨动次数
1.确定状态
F[i][j]表示从i项到j项拨动的最少次数
那么结果为F[1][N](或者F[N][1])
2.确定状态转移方程和边界条件
i项到i项,最少1次拨动。F[i][i]=1。
迭代法:
需要找到迭代关系,F[i][j-1]和F[i][j]。
在不考虑A[j]和F[i][j-1]一起拨动的情况下,F[i][j]=F[i][j-1]+F[j][j]。
根据题意存在下面几种情况:
这几种情况是可以同时存在的,每个情况都是独立的。
第一种,A[j]=A[j-1],F[i][j]=min(F[i][j-1],F[i][j])
第二种,A[j]=A[i],F[i][j]=min(F[i+1][j-1]+F[i][i],F[i][j])。
第三种,A[i]=A[i+1],F[i][j]=min(F[i+1][j],F[i][j])。
这种情况在开始的时候漏了,但是在测试20 11233223322211344444数据的时候,发现动态规划表对应不上。
所以动态规划处理的时候,最麻烦的就是情况得罗列清楚,每一种可能影响当前最优解的情况都得设计出来。
第四种,A[j]=A[i...j]项的其中一个,F[i][j]=min(F[i][k], F[k+1][j]) k=[i...j]

根据样例12321得到3画出F[i][j]的迭代表
	1	2	3	2	1

ij	1	2	3	4	5
1	1	2	3	3	3
2		1	2	2	3
3			1	2	3
4				1	2
5					1
迭代表求的时候,[i][i]先确定为1。
然后第一项求[1][2]。([1][3]不能接着求,因为需要有[2][3]的值才能确定[1][3]。)
所以第二项求[2][3],然后[1][3]。
接着[3][4],然后[2][4],然后[1][4]。
最后是[4][5],然后[3][5],然后[2][5],最后[1][5]。
所以最后结果是[1][5]为3。
测试样例
20
11233223322211344444
1 1 2 3 3 3 3 4 4 4 4 4 4 4 5 6 6 6 6 6
0 1 2 3 3 3 3 4 4 4 4 4 4 4 5 6 6 6 6 6
0 0 1 2 2 2 2 3 3 3 3 3 4 4 5 6 6 6 6 6
0 0 0 1 1 2 2 2 2 3 3 3 4 4 4 5 5 5 5 5
0 0 0 0 1 2 2 2 2 3 3 3 4 4 4 5 5 5 5 5
0 0 0 0 0 1 1 2 2 2 2 2 3 3 4 5 5 5 5 5
0 0 0 0 0 0 1 2 2 2 2 2 3 3 4 5 5 5 5 5
0 0 0 0 0 0 0 1 1 2 2 2 3 3 3 4 4 4 4 4
0 0 0 0 0 0 0 0 1 2 2 2 3 3 3 4 4 4 4 4
0 0 0 0 0 0 0 0 0 1 1 1 2 2 3 4 4 4 4 4
0 0 0 0 0 0 0 0 0 0 1 1 2 2 3 4 4 4 4 4
0 0 0 0 0 0 0 0 0 0 0 1 2 2 3 4 4 4 4 4
0 0 0 0 0 0 0 0 0 0 0 0 1 1 2 3 3 3 3 3
0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 3 3 3 3
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 2 2 2
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
*/
int main() {
	while (1) {
		int dp[101][101] = {0};
		int n;
		cin >> n;
		char a[101];
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
			dp[i][i] = 1;
		}
		for (int j = 2; j <= n; j++) {
			for (int i = j - 1; i >= 1; i--) {
				dp[i][j] = dp[i][j - 1] + dp[j][j];
				if (a[j] == a[j - 1]) {
					dp[i][j] = min(dp[i][j - 1], dp[i][j]);
				}
				if (a[j] == a[i]) {
					dp[i][j] = min(dp[i + 1][j - 1] + dp[j][j], dp[i][j]);
				}
				if(a[i]==a[i+1]){
					dp[i][j] = min(dp[i + 1][j], dp[i][j]);
				}
				for (int k = i; k < j; k++) {
					dp[i][j] = min(dp[i][k] + dp[k + 1][j], dp[i][j]);
				}
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				cout << dp[i][j] << " ";
			}
			cout << endl;
		}
		cout << dp[1][n] << endl;
	}
	return 0;
}


Python写法

n = int(input())
s = input()
s = ' ' + s
dp = [[0 for i in range(101)] for j in range(101)]
"""
dp=[]
for i in range(101):
    dp.append([])
    for j in range(101):
        dp[i].append(0)
"""
"""
动态规划处理,dp[i][j]表示第i项到第j项的最少拨动次数
dp[i][i]=1
迭代法:迭代关系dp[i][j]=dp[i][j-1]+1当没有可同时拨动的情况
特殊情况:每种情况都是按照a[j]是需要额外拨动来看
1.当s[j]=s[j-1],dp[i][j]=min(dp[i][j-1],dp[i][j])(dp[i][j]和dp[i][j-1]不一定相等的,每个状态都是单独的,注意区分)
2.当s[j]=s[i],dp[i][j]=min(dp[i+1][j-1]+1, dp[i][j]) (注意dp[i][j-1]和dp[i+1][j-1]不一定相等的,每个情况都是独立的)
3.当s[i]=s[i+1],dp[i][j]=min(dp[i+1][j], dp[i][j])
4.因为s[j]可能等于s[i...j]的一项,所以会造成分段dp[i][j]=min(dp[i][j], dp[i][k]+dp[k+1][j]) k=[i...j]
样例:
5 12321结果3
20 11233223322211344444结果6
"""
for j in range(1, n + 1):
    for i in range(j, 0, -1):
        if j == i:
            dp[i][j] = 1
            continue
        dp[i][j] = dp[i][j - 1] + 1
        if s[j] == s[j - 1]:
            dp[i][j] = min(dp[i][j - 1], dp[i][j])
        if s[j] == s[i]:
            dp[i][j] = min(dp[i + 1][j - 1] + 1, dp[i][j])
        if s[i] == s[i + 1]:
            dp[i][j] = min(dp[i + 1][j], dp[i][j])
        for k in range(i, j):
            dp[i][j] = min(dp[i][k] + dp[k + 1][j], dp[i][j])

for i in range(1, n + 1):
    for j in range(1, n + 1):
        print(dp[i][j], end=' ')
    print()
print(dp[1][n])

posted @ 2024-05-09 15:06  Danlis  阅读(130)  评论(0编辑  收藏  举报