9.力扣2018年常见编程题总结(动态规划)

1.找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 的长度。

输入: s = "ababbc", k = 2

输出: 5

最长子串为 "ababb" ,其中 'a' 重复了 2 次, 'b' 重复了 3 次。

解:当x字符的次数小于k时则不能最终结果包括x,因此将x分为左边和右边进行递归寻找

代码:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
using namespace std;

class Solution {
public:
	int ans = 0;
	void process(string s, int k)
	{
		//vis用来统计每个字符出现的次数
		int vis[26] = { 0 };
		for (int i = 0; i < s.size(); ++i)
			vis[s[i] - 'a']++;

		//对x左边的字符串进行判断
		int l = 0, flag = 1;
		for (int i = 0; i < s.size(); ++i)
		{
			if (vis[s[i] - 'a'] && vis[s[i] - 'a'] < k)
			{
				if (i > l) process(s.substr(l, i - l), k);
				flag = 0;
				l = i + 1;
			}
		}
		if (!flag && s.size() > l) process(s.substr(l, s.size() - 1), k);
		if (flag) ans = max((int)s.size(), ans);
	}
	int longestSubstring(string s, int k) {
		process(s, k);
		return ans;
	}
};

int main()
{
	Solution s1;
	string s = "ababbc";
	cout << s1.longestSubstring(s,2);
	return 0;
}

2.给定一个非空二叉树,返回其最大路径和。

本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

输入: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

输出: 42

解:给定一个非空节点,最终路径经过这个节点有4种情况:1.只有该节点本身(左右子树的路径都是负数);2.该节点+左子树路径;3.该节点+右子树路径;4.该节点+左子树路径+右子树路径。其中1,2,3都可以作为子树路径和向上延伸,而4则不行。

代码:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
using namespace std;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
	int result = INT_MIN;
	int maxPathSum(TreeNode* root)
	{
		getPath(root);
		return result;//result存储搜索过路径的最大值
	}
	int getPath(TreeNode* node) {
		if (node == NULL) return 0;
		int left = getPath(node->left);	//向左不断递归
		int right = getPath(node->right);	//向右不断递归
		int tmp = max(max(left + node->val, right + node->val), node->val);//当前结点的最大路径值
		result = max(result, max(tmp, left + right + node->val));//历史所有结点的最大路径值
		return tmp;
	}
};

int main()
{
	Solution s1;
	TreeNode *root = new TreeNode(2);
	root->left = new TreeNode(1);
	root->right = new TreeNode(3);
	cout << s1.maxPathSum(root);
	return 0;
}

3.

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)

示例:

输入: [100, 4, 200, 1, 3, 2]

输出: 4

解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

解:先将数组插入到无序集合中,利用count方法来进行元素寻找。从第i个元素开始寻找,且保证i-1元素没有找到,每找到一个元素,则curmax++

代码:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
public:
	int longestConsecutive(vector<int>& nums) {
		unordered_set<int> dict;
		for (auto i : nums)
			dict.insert(i);//将元素插入到集合中
		int res = 0;
		int cur, curMax;
		for (auto i : nums) {
			if (dict.count(i - 1) == 0) {//从当前元素开始寻找,且前一个元素没有找到
				cur = i;
				curMax = 1;//找到的连续个数
				while (dict.count(cur + 1)) {//找下一个
					cur++;
					curMax++;
				}
				res = max(res, curMax);//历史最大值
			}
		}
		return res;
	}
};

int main()
{
	Solution s1;
	vector<int> a = { 100, 4, 200, 1, 3, 2 };
	cout << s1.longestConsecutive(a);
	return 0;
}

4.你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]

输出: 4

解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。   偷窃到的最高金额 = 1 + 3 = 4 。

解:通过维护dp数组来存放当前累计的最大值。

代码:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
public:
	int rob(vector<int>& nums) {
		int length = nums.size();
		if (length == 0) return 0;
		if (length == 1) return nums[0];
		if (length == 2)	return max(nums[0], nums[1]);
		int *dp = (int *)malloc(length * sizeof(int));
		dp[0] = nums[0]; dp[1] = nums[1]; dp[2] = dp[0] + nums[2];
		int k = max(dp[1],dp[2]);
		for (int i = 3; i < length; i++)
		{
			dp[i] = max(dp[i - 2] , dp[i-3])+nums[i];//动态方程
			k = max(k, dp[i]);
		}
		return k;
	}
};

int main()
{
	Solution s1;
	vector<int> a = { 2,1,1,2 };
	cout << s1.rob(a);
	return 0;
}

5.给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12

输出: 3

解释: 12 = 4 + 4 + 4.

解:利用动态规划进行求解,先把小于n的完全平方数进行统计,放置在dp表中,之后进行最小个数的统计。从1到n遍历求解最小组成个数,再对每个数遍历小于其的所有完全平方数,最小组成个数的状态转移方程为:dp[i] = min(dp[i], dp[i - j * j] + 1)

代码:

using namespace std;

class Solution {
public:
	int numSquares(int n) {
		vector<int> dp(n + 1, INT_MAX);
		for (int i = 1; i * i <= n; i++)
			dp[i * i] = 1;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j * j < i; j++)
				dp[i] = min(dp[i], dp[i - j * j] + 1);
		return dp[n];
	}
};

int main()
{
	Solution s1;
	cout << s1.numSquares(13);
	return 0;
}

6.给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]

输出: 4

解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

解:利用动态规划维护一张dp表,记录第i个数下递增的总个数。

using namespace std;

class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {
		int length = nums.size();
		if (length == 0)	 return 0;
		int * dp = new int[length];
		for (int i = 0; i < length; i++)
			dp[i] = 1;
		for (int i = 0; i < length; i++)
		{
			int current = nums[i];
			for (int j = 0; j < i; j++)
			{
				if (current > nums[j])//递增才进行记录
					dp[i] = max(dp[j] + 1, dp[i]);//每次遍历记录找到的最大个数
			}
		}
		int res = dp[0];
		for (int i = 0; i < length; i++)
		{
			res = max(res, dp[i]);
		}
		return res;
	}
};

int main()
{
	Solution s1;
	vector<int> ss = { 10,9,2,5,3,7,101,18 };
	cout << s1.lengthOfLIS(ss);
	return 0;
}

7.给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1

示例 1:

输入: coins = [1, 2, 5], amount = 11

输出: 3

解释: 11 = 5 + 5 + 1

示例 2:

输入: coins = [2], amount = 3

输出: -1

解:利用动态规划从amount=0开始对硬币进行寻找。

代码:

using namespace std;

class Solution {
public:
	int coinChange(vector<int>& coins, int amount) {
		int* dp = new int[amount + 1]();
		for (int i = 1; i < amount + 1; i++)
			dp[i] = 1000;
		dp[0] = 0;
		for (int i = 0; i <= amount; i++)
		{
			for (int j = 0; j < coins.size(); j++)
			{
				if (i - coins[j] >= 0)
					if (dp[i] > dp[i - coins[j]] + 1)
						dp[i] = dp[i - coins[j]] + 1;
			}
		}
		if (dp[amount] >= 1000)
			return -1;
		else
			return dp[amount];
	}
};

int main()
{
	Solution s1;
	vector<int> ss = { 186,419,83,408};
	cout << s1.coinChange(ss, 6249);
	return 0;
}

8.给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ]

输出: 4

解释: 最长递增路径为 [1, 2, 6, 9]

using namespace std;

class Solution {
	vector<vector<int>> pos{ {-1,0},{1,0},{0,-1},{0,1} };
public:
	int longestIncreasingPath(vector<vector<int>>& matrix) {
		if (matrix.empty() || matrix[0].empty()) return 0;
		//创建一个二维表
		vector<vector<int>> visited(matrix.size(), vector<int>(matrix[0].size(), 0));
		int longest = 0;//记录最长路径
		int current = 1;
		for (int i = 0; i < matrix.size(); i++)
		{
			for (int j = 0; j < matrix[0].size(); j++)
			{
				if (visited[i][j] == 0)
					dfs(matrix, visited, current, longest, i, j);//深度优先搜索
			}
		}
		return longest;
	}

	void dfs(vector<vector<int>>& matrix, vector<vector<int>>& visited, int &current, int &longest, int i, int j)
	{
		visited[i][j] = 1;
		int oldlen = current;
		int maxlen = current;
		for (int m = 0; m < 4; m++)
		{
			int x = i + pos[m][0];
			int y = j + pos[m][1];//向四个方向寻找
			if (x < 0 || y < 0 || x >= matrix.size() || y >= matrix[0].size() || matrix[x][y] <= matrix[i][j]) continue;
			else if (visited[x][y])
				maxlen = max(maxlen, oldlen + visited[x][y]);
			else
			{
				current++;
				dfs(matrix, visited, current, maxlen, x, y);
				maxlen = max(maxlen, current);
				current = oldlen;
			}
		}
		visited[i][j] = max(maxlen - oldlen + 1, visited[i][j]);
		longest = max(longest, maxlen);
	}
};

int main()
{
	Solution s1;
	vector<vector<int>> ss = { {9,9,4},{6,6,8 },{2,1,1 }};
	cout << s1.longestIncreasingPath(ss);
	return 0;
}

 

 

 

 

 

 

 

 

 

 

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