藍橋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])

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章