算法练习(7) —— 动态规划 Strange Printer

本文通过LeetCode第664题StrangePrinter探讨了动态规划的应用,详细介绍了如何找到有效的状态转移方程并给出了非递归实现的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法练习(7) —— 动态规划 Strange Printer

动态规划

动态规划算法通常处理的是多阶段的决策最优化问题。挺多的问题都含有递推的思想。做这样的问题,最重要的就是找到对应的状态转移方程。也就是找到了对应的递推公式/递归公式,问题就可以迎刃而解。往往动态规划的实际代码并不算很复杂(至少我现在做到的是这样),但是要理解起来确实有点难度。

习题

本次题目取自leetcode中的 Dynamic Programming 栏目中的第664题:
Strange Printer


题目如下:

Description:

There is a strange printer with the following two special requirements:

  1. The printer can only print a sequence of the same character each time.

  2. At each turn, the printer can print new characters starting from and ending at any places, and will cover the original existing characters.

Given a string consists of lower English letters only, your job is to count the minimum number of turns the printer needed in order to print it.

Examples:

Example 1:

Input: “aaabbb”
Output: 2
Explanation: Print “aaa” first and then print “bbb”.

Example 2:

Input: “aba”
Output: 2
Explanation: Print “aaa” first and then print “b” from the second place of the string, which will cover the existing character ‘a’.

Hint:

Length of the given string will not exceed 100.

思路与代码

  • 首先理解一下题意:大概意思就是打印机每一次只能打印同一个字母,字母数不限。而且每一次起始打印位置不限,它可以在任意地方开始,并且覆盖原来的字母。让你求打印出要求字符所需的最少次数。
  • 可以按照给的例子分析一下:
    第一个输入”aaabbb”可以由”aaaaaa”上面再打印3个”bbb”覆盖”aaa”达成。
  • 那么首先可以想到的状态转移就是本次和上次的差别。但是由于2次字符串的差别有可能非常大,并不适合作状态转移方程的判断。
  • 那么用字符串长度从1开始不断增加的递推呢?这是有可行性的,因为2个相邻的字母相同或者不同都有对应的关系。但是仔细想一下,反向使用递归可能更容易归纳公式和理解。
  • 在这里采用的想法是:从目标串,不断的分出小子串去计算,以达到目的。那么用什么来作为分割的标准呢,比较容易想到的就是分割点的字母跟起点或者跟终点的字母相同。因为如果分割点跟终点的字母相同,可以理解为,分割点和终点之间在上一步都是一样的字母,下一步又被其他的字母染过去了(或者没被染)。分割点d,起点start,终点end。那么
    dp[start][end] = dp[start][d] + dp[d + 1][end] - 1 (s[d] == s[end])
  • 当然普通的情况也要考虑。这样的话状态转移方程就得到了,程序实现起来就容易啦~

这里用非递归方法来取代递归,代码如下:

#include <string>
#include <algorithm>
using namespace std;


class Solution {
public:
    int strangePrinter(string s) {
        int n = s.length();
        if (n == 0) return 0;

        int dp[101][101];
        memset(dp, 0, sizeof(dp));

        for (int i = 0; i < n; i++)
            dp[i][i] = 1;

        for (int i = 1; i < n; i++) {
            for (int start = 0; start < n - i; start++) {
                int end = start + i;
                dp[start][end] = i + 1;
                for (int k = start; k < end; k++) {
                    int sum = dp[start][k] + dp[k + 1][end];
                    if (s[k] == s[end])
                        sum--;
                    dp[start][end] = dp[start][end] > sum ? sum : dp[start][end];
                }
            }
        }
        return dp[0][n - 1];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值