动态规划续集


遇到矩阵,网络,字符串间的比较题,单序列(一维)动规解决不了的情况下,就需要考虑双序列(二维)动规。

例七 路径总数

机器人在(m*n)网格的左上角,可以向下,向右走,走到网格的右下角,有多少种可能的路径。
状态:F(i,j):走到(i,j)时有多少种可能的路径
递推:中间:F(i,j) = F(i-1,j)+F(i,j-1)
初始化:
第0列:F(i,0) = 1;
第0行:F(0,i) = 1;
返回结果:
F(m-1)(n-1)

#include<iostream>
#include<vector>
using namespace std;
int uniquePaths(int m, int n)
{
	if (m < 1 || n < 1)
		return 0;
	//初始化  F(0,i) = F(i,0) = 1;
	vector<vector<int>> path(m, vector<int>(n, 1));
	//递推
	for (int i = 1; i < m; i++)
	{
		for (int j = 1; j < n; j++)
		{
			path[i][j] = path[i - 1][j] + path[i][j - 1];
		}
	}
	return path[m - 1][n - 1];
}
int main()
{
	cout << uniquePaths(5,5) << endl;
	system("pause");
	return 0;
}

例八 路径总数之路径上有坑

机器人走网格,网格上有坑,不能走
【0,0,0】
【0,1,0】
【0,0,0】
1不能走,共有两条路径
状态:F(i,j):从(0,0)走到(i,j)时有多少种可能的路径
递推:中间:F(i,j):if (i,j)==1 —>F(i,j) = 0
else F(i,j)= F(i-1,j)+F(i,j-1)
初始化:同样要判断(i,j)是否为1,如果为1,后面就都去不了了,因为只能往右,往下走
第0列:F(i,0) = 1;
第0行:F(0,i) = 1;
返回结果:
F(m-1)(n-1)

#include<iostream>
#include<vector>
using namespace std;
int uniquePathsWithObstacles(vector<vector<int>> & obstacleGrid)
{
	if (obstacleGrid.empty())
		return 0;
	int m = obstacleGrid.size();
	int n = obstacleGrid[0].size();
	vector<vector<int>> paths(m, vector<int>(n, 0));
	//初始化   F(0,0)
	//第一列初始化
	for (int i = 0; i < m; i++)
	{
		if (obstacleGrid[i][0])
			break;
		else
			paths[i][0] = 1;
	}
	//第一行
	for (int i = 0; i < n; i++)
	{
		if (obstacleGrid[0][i])
			break;
		else
			paths[0][i] = 1;
	}
	//递推
	for (int i = 1; i < m; i++)
	{
		for (int j = 1; j < n; j++)
		{
			if (obstacleGrid[i][j])
				paths[i][j] = 0;
			else
				paths[i][j] = paths[i - 1][j] + paths[i][j - 1];
		}
	}
	return paths[m - 1][n - 1];
}
int main()
{
	vector<vector<int>> obstaclesGrid = { {0,0,0},{0,1,0},{0,0,0} };
	cout << uniquePathsWithObstacles(obstaclesGrid) << endl;
	system("pause");
	return 0;
}

例九 矩阵最小路径和

一个非负矩阵,从左上角到右下角的最小路径和
状态:F(i,j):从(0,0)到(i,j)的最小路径和
递推:
第一行:F(0,i)=F(0,i-1)+(i,j)
第一列:F(i,0)=F(i-1,0)+(i,j)
其他位置:F(i,j) = min(F(i-1,j), F(i,j-1))+ (i,j)
初始化:F(0,0) = (0,0)
返回结果:F(m-1,n-1)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int FindMinBoard(vector<vector<int>> & board)
{
	if (board.empty())
		return 0;
	int m = board.size();
	int n = board[0].size();
	vector<vector<int>> min_sum(m, vector<int>(n, 0));
	//初始化
	min_sum[0][0] = board[0][0];
	//第一列递推
	for (int i = 1; i < m; i++)
		min_sum[i][0] = board[i][0]+min_sum[i-1][0];
	//第一行递推
	for (int i = 1; i < n; i++)
		min_sum[0][i] = board[0][i]+min_sum[0][i-1];
	//递推
	for (int i = 1; i < m; i++)
	{
		for (int j = 1; j < n; j++)
		{
			min_sum[i][j] = min(min_sum[i - 1][j], min_sum[i][j - 1]) + board[i][j];
		}
	}
	//返回结果
	return min_sum[m - 1][n - 1];
}
int main()
{
	vector<vector<int>> board{ {1,2,3},{4,5,6},{7,8,9} };
	cout << FindMinBoard(board) << endl;
	system("pause");
	return 0;
}

例十 回文串分割

有一个字符串s,把s分割成一系列的子串,分割的每一个子串都为回文串,返回最小的分割次数(回文串左右对称)。
状态:F(i):前i个字符的最小分割数
递推:j<i && F(i):min(F(j)+1,F(i)) && substr[j+1,i)是回文串
初始化:F(i)=i-1,F(0)=-1
返回结果:F(i)

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

bool isPal(string s, int i, int j)
{
	while (i < j)
	{
		if (s[i] != s[j])
			return false;
		i++;
		j--;
	}
	return true;
}
int MinOfCut(string & s)
{
	if (s.empty())
		return -1;
	int n = s.size();
	vector<int> cut(n+1,0);
	//初始化  F[i] = i-1
	for (int i = 0; i < n + 1; i++)
		cut[i] = i - 1;//最大分割数
	//递推
	for (int i = 1; i < n + 1; i++)
	{
		for (int j = 0; j < i; j++)
		{
			//j<i
			//substr[j+1,i)是回文串
			if (isPal(s, j, i - 1))
			{
				//F(i) = min(F(j)+1,F(i))
				cut[i] = min(cut[j] + 1, cut[i]);
			}
		}
	}
	return cut[n];
}
int main()
{
	string s("aaabaa");
	cout << MinOfCut(s) << endl;
	system("pause");
	return 0;
}

用动态规划来判断一个字符区间的子串是否为回文串的版本:
状态:F[i,j]是否为回文串
递推:F(i,j):s[i] == s[j] && F(i+1,j-1)-------从后面做起始
初始化:单字符都是回文的,F(i,i)=true
返回结果:返回F(i,j)矩阵,只更新一半(i<=j)

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
vector<vector<bool>> is_PalDP(string s)
{
	int n = s.size();
	vector<vector<bool>> is_pal(n, vector<bool>(n, false));
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = i; j < n; j++)
		{
			if (j == i)
				is_pal[i][j] = true;//F[i][i] = true   初始化
			else if (j == i + 1)//连续两个字符
				is_pal[i][j] = (s[i] == s[j]);
			else  //j>i+1
				is_pal[i][j] = (s[i] == s[j] && is_pal[i + 1][j - 1]);
		}
	}
	return is_pal;
}
int MinOfCut(string & s)
{
	if (s.empty())
		return -1;
	int n = s.size();
	vector<int> cut(n + 1, 0);
	//初始化  F[i] = i-1
	for (int i = 0; i < n + 1; i++)
		cut[i] = i - 1;
	vector<vector<bool>> is_pal = is_PalDP(s);
	//递推
	for (int i = 1; i < n + 1; i++)
	{
		for (int j = 0; j < i; j++)
		{
			//j<i
			//substr[j+1,i)是回文串
			if (is_pal[j][i - 1])
			{
				//F(i) = min(F(j)+1,F(i))
				cut[i] = min(cut[j] + 1, cut[i]);
			}
		}
	}
	return cut[n];
}
int main()
{
	string s("aaabaa");
	cout << MinOfCut(s) << endl;
	system("pause");
	return 0;
}

简单的动态规划问题,状态,状态递推和状态初始化都比较直观。对于复杂的动态规划问题,状态,状态递推和状态初始化都比较隐含,需要仔细推敲。尤其是状态递推可能需要额外的辅助判断条件才能达成。

例十一 编辑距离

从一个字符串word1变到另一个字符串word2的最小操作数(插入一个字符、删除一个字符、替换一个字符)
状态:把word1的前i个字符转成word2的前j个字符
递推:
替换:if(word1[i] == word2[j]) ---->F(i,j) = F(i-1,j-1) else : F(i,j) = F(i-1,j-1)+1
插入:少一个才插入一个,F(i-1,j)=F(i-1,j-1)+1 F(i,j)=F(i,j-1)+1; xa–>xav
删除:多一个才删除一个,F(i,j-1)=F(i-1,j-1)+1 F(i,j)=F(i-1,j)+1; xat—>xa
在这里插入图片描述
递推公式:F(i,j)=min{F(i,j-1)+1,F(i-1,j)+1, F(i-1,j-1)+((w1[i]==w2[j])?0:1)}
初始化:空状态–空串
F(i,0)=i 字符串转化为空串,删除操作
F(0,i)=i 空串转化为字符串,插入操作
返回结果:F(m,n)

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

int minDistance(string word1, string word2)
{
	if (word1.empty() || word2.empty())
		return max(word1.size(), word2.size());

	int m = word1.size();
	int n = word2.size();
	vector<vector<int>> min_dis(m + 1, vector<int>(n + 1, 0));
	//初始化 F(i,0) F(0,i)
	for (int i = 0; i < m + 1; i++)
		min_dis[i][0] = i;
	for (int i = 0; i < n + 1; i++)
		min_dis[0][i] = i;
	//递推
	for (int i = 1; i < m + 1; i++)
	{
		for (int j = 1; j < n + 1; j++)
		{
			//F(i,j)=min{F(i,j-1)+1,F(i-1,j)+1, F(i-1,j-1)+((w1[i]==w2[j])?0:1)}    插入 删除 替换
			min_dis[i][j] = min(min_dis[i - 1][j], min_dis[i][j - 1]) + 1;
			if (word1[i - 1] == word2[j - 1])
				min_dis[i][j] = min(min_dis[i][j], min_dis[i - 1][j - 1]);
			else
				min_dis[i][j] = min(min_dis[i][j], min_dis[i - 1][j - 1]+1);
		}
	}
	//返回结果
	return min_dis[m][n];
}
int main()
{
	cout << minDistance("haha", "hehe") << endl;
	system("pause");
	return 0;
}

例十二 不同子序列

给定两个字符串S和T,求S有多少个不同的子序列与T相同,子序列可以不连续,但是相对位置不能变

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值