算法练习(7) —— 动态规划 Strange Printer
动态规划
动态规划算法通常处理的是多阶段的决策最优化问题。挺多的问题都含有递推的思想。做这样的问题,最重要的就是找到对应的状态转移方程。也就是找到了对应的递推公式/递归公式,问题就可以迎刃而解。往往动态规划的实际代码并不算很复杂(至少我现在做到的是这样),但是要理解起来确实有点难度。
习题
本次题目取自leetcode中的 Dynamic Programming
栏目中的第664题:
Strange Printer
题目如下:
Description:
There is a strange printer with the following two special requirements:
The printer can only print a sequence of the same character each time.
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];}
};