数列DP基础

目录

一,常见数列DP的通项公式

1,单数列的DP问题

2,双数列的DP问题

3,多数列的DP问题

二,最长上升子序列

CSU 1047 最长上升子序列

CSU 1225 ACM小组的队列

POJ 2533 Longest Ordered Subsequence

OpenJ_Bailian 2945 拦截导弹(最长递减子数列)

力扣 300. 最长上升子序列

力扣 673. 最长递增子序列的个数

 合唱队形安排问题

三,最大子段和、最大子段积

HDU 1003、NBUT 1090 Max Sum

力扣 剑指 Offer 42. 连续子数组的最大和

力扣 152. 乘积最大子序列

力扣 1186. 删除一次得到子数组最大和

四,(最长)公共子序列

HDU 5791 Two(公共子序列的数目)

POJ 1458 Common Subsequence(最长公共子序列)

CSU 1060 Nearest Sequence (三个数组的最长公共子序列)

力扣 1143. 最长公共子序列

五,超递增数列

CSU 1203 Super-increasing sequence

六,超最大字段和

HDU 1244 Max Sum Plus Plus Plus

七,else

力扣 1218. 最长定差子序列

力扣 1458. 两个子序列的最大点积

力扣 剑指 Offer II 091. 粉刷房子

力扣 276. 栅栏涂色

力扣 368. 最大整除子集

力扣 1911. 最大子序列交替和

力扣 2140. 解决智力问题

力扣 2327. 知道秘密的人数

力扣 651. 四个键的键盘

力扣 1259. 不相交的握手

力扣 198. 打家劫舍

力扣 2369. 检查数组是否存在有效划分

力扣 1997. 访问完所有房间的第一天


一,常见数列DP的通项公式

本文都是常见的数列DP,他们有着如下的通项公式。

对于稍微复杂点的数列DP,都放在数列DP进阶中了。

1,单数列的DP问题

常见公式:f(x) = g(f(x-c1), n)或f(x) = g(f(x-c1), f(x-c2),n),这里的c1和c2是常数,一般c1=1,c2=2

g可能包含了其他关于n的非解析函数,比如g(f(n-1), n) = f(n-1) + array[n] + n^2,即包含了数组array的信息。

template<typename T=int, typename T2=int>
class ArrayDP
{
public:
	ArrayDP(const vector<T>&data, int n)//n表示要求解的范围是[0,n)
	{
		ans.resize(n);
		m = n;
		this->data = data;
	}
	void getAllAns()
	{
		initAns();
		for (int i = lastValueId + 1; i < m; i++)recur(i);
	}
	inline T2 getAns(int id)
	{
		if (id < firstValidId || id < 0 || id >= ans.size())return invalidAns;
		return ans[id];
	}
protected:
	virtual void initAns()
	{
		// 初始化firstValidId和lastValueId和invalidAns,并初始化ans的[firstValidId,lastValueId]这一段的值(最少1个)
	}
	virtual void recur(int id)
	{
		//递推式 num[id] = ......
	}
protected:
	int m;
	int firstValidId = 0;
	int lastValueId = 0;
	T2 invalidAns = T2{};
	vector<T>data;
	vector<T2>ans;
};

2,双数列的DP问题

通项公式:f(x,y) = g(f(x,y-c1), f(x-c2,y), f(x-c3,y-c4), x, y),一般c1=c2=c3=c4=1

3,多数列的DP问题

形式上和双数列的DP问题的通项公式一致。

二,最长上升子序列

class DP_LengthOfLIS :public ArrayDP<int, pair<int, int>>
{
public:
	DP_LengthOfLIS(const vector<int>&data, int n, int p = INT_MAX) : ArrayDP(data, n)
	{
        this->p=p;
		getAllAns();
		for (int i = 0; i < n; i++)if (len< getAns(i).first)len = getAns(i).first, num = getAns(i).second;
		else if (len == getAns(i).first)num += getAns(i).second;
	}
	int getLengthOfLIS()
	{
		return len;
	}
	int getNumOfLIS()
	{
		return num;
	}
private:
	void initAns()
	{
		ans[0] = { 1,1 };
	}
	void recur(int i)
	{
		ans[i] = { 1, 1 };
		for (int j = 0; j < i; j++)
		{
			if (data[j] < data[i]) {
				if (ans[i].first < ans[j].first + 1)ans[i].first = ans[j].first + 1, ans[i].second = ans[j].second;
				else if (ans[i].first == ans[j].first + 1)ans[i].second += ans[j].second, ans[i].second %= p;
			}
		}
	}
	int len, num,p;
};

CSU 1047 最长上升子序列

题目:

Description

  名词解释:

一串数字比如1、5、3、6、9、8、10,它的子序列是从左到右不连续的若干个数,比如1、5、6,3、9、8、10都是它的子序列。

最长上升子序列即从左到右严格增长的最长的一个子序列,1、5、6、9、10就是这个序列的一个最长上升子序列。

给出若干序列,求出每个序列的最长上升子序列长度。

Input

  多组数据,每组第一行正整数n,1 <= n <= 1000,第二行n个空格隔开的不大于1,000,000的正整数。

Output

 每组数据输出一行,最长上升子序列的长度。

Sample Input

7
1 5 3 6 9 8 10

Sample Output

5

代码:

#include <iostream>
#include <vector>
#include <string.h>
#include <limits.h>
using namespace std;

int main()
{
	int k;
	vector<int>h(1001);
	while (cin >> k)
	{
		for (int i = 0; i < k; i++)cin >> h[i];
		cout << DP_LengthOfLIS(h, h.size()).getLengthOfLIS() << endl;
	}
	return 0;
}

CSU 1225 ACM小组的队列

题目:

Description

ACM小组每次出去活动都要排队,但是大家总是不想按照任何规则来排好这个队伍(大概是因为每个人都比较有个性,例如Gestapolur),所以每次队 伍都是乱的,不过Samsara今天突然想要计算一下队伍中按身高升序排列的最长子队列,而且还想知道最长的子队列的个数。所谓子队列,就是在队列 A_1...A_i...A_n中的一个队列A_p[1]...A_p[2]..A_p[m],(1<=p[1]<p[2]<...& lt;p[m]<=n,m<=n)。Samsara的统计学得非常不好,所以又要麻烦会编程的你来帮他解决这个难题了。

Input

每组数据两行,第一行一个正整数n(1<=n<=1000),表示有n个人,第二行有用空格隔开的n个正整数A_1...A_i..A_n(1<=A_i<=200),表示这n个人的身高。

Output

输出对应也有若干行,请输出符合要求的最长子队列的长度和个数,以空格隔开。

Sample Input

5
1 4 2 6 3

Sample Output

3 3

代码:

#include <iostream>
#include <vector>
#include <string.h>
#include <limits.h>
using namespace std;



int main()
{
	int k;
	vector<int>h(1001);
	while (cin >> k)
	{
		for (int i = 0; i < k; i++)cin >> h[i];
		DP_LengthOfLIS opt(h, h.size());
		cout << opt.getLengthOfLIS()<<" "<< opt.getNumOfLIS() << endl;
	}
	return 0;
}

POJ 2533 Longest Ordered Subsequence

题目:同CSU 1047 最长上升子序列

但是POJ编译器太老了,模板代码编不过。

代码:

#include<iostream>
using namespace std;
 
int list[1000];
int maxs[1000];
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> list[i];
		maxs[i] = 1;
	}
	for (int i = 1; i < n; i++)for (int j = 0; j < i; j++)
	  if (list[j] < list[i] && maxs[i] < maxs[j] + 1)maxs[i] = maxs[j] + 1;
	int m = 0;
	for (int i = 0; i < n; i++)if (m < maxs[i])m = maxs[i];
	cout << m;
	return 0;
}

OpenJ_Bailian 2945 拦截导弹(最长递减子数列)

题目:

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

Input

输入有两行, 
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25), 
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。

Output

输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。 
Sample Input

8
300 207 155 300 299 170 158 65

Sample Output

6

代码:

#include <iostream>
#include <vector>
#include <string.h>
#include <limits.h>
using namespace std;

class DP_LengthOfLIS :public ArrayDP<int, pair<int, int>>
{
public:
	DP_LengthOfLIS(const vector<int>&data, int n, int p = INT_MAX) : ArrayDP(data, n)
	{
		this->p = p;
		getAllAns();
		len = num = 0;
		for (int i = 0; i < n; i++)if (len < getAns(i).first)len = getAns(i).first, num = getAns(i).second;
		else if (len == getAns(i).first)num += getAns(i).second;
	}
	int getLengthOfLIS()
	{
		return len;
	}
	int getNumOfLIS()
	{
		return num;
	}
private:
	void initAns()
	{
		ans[0] = { 1,1 };
	}
	void recur(int i)
	{
		ans[i] = { 1, 1 };
		for (int j = 0; j < i; j++)
		{
			if (data[j] <= data[i]) { //改了一行
				if (ans[i].first < ans[j].first + 1)ans[i].first = ans[j].first + 1, ans[i].second = ans[j].second;
				else if (ans[i].first == ans[j].first + 1)ans[i].second += ans[j].second, ans[i].second %= p;
			}
		}
	}
	int len, num, p;
};


int main()
{
	int k;
	vector<int>h(1001);
	while (cin >> k)
	{
		for (int i = 0; i < k; i++)cin >> h[i], h[i] *= -1;
		cout << DP_LengthOfLIS(h, k).getLengthOfLIS();
	}
	return 0;
}

力扣 300. 最长上升子序列

题目:

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

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

代码:

class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {
		return DP_LengthOfLIS(nums, nums.size(),1000000007).getLengthOfLIS();
	}
};

力扣 673. 最长递增子序列的个数

给定一个未排序的整数数组,找到最长递增子序列的个数。

示例 1:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:

输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
注意: 给定的数组长度不超过 2000 并且结果一定是32位有符号整数。

class Solution {
public:
	int findNumberOfLIS(vector<int>& nums) {
		if (nums.size() == 0)return 0;
		return DP_LengthOfLIS(nums, nums.size()).getNumOfLIS();
	}
};

 合唱队形安排问题

【问题描述】N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,  则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

#include<iostream>
using namespace std;

//计算以list[i]为结尾的递增数组的最大长度length[i]
void getMaxLength(int n, double *list, int *length)
{
	length[0] = 0;
	for (int i = 1; i <= n; i++)
	{
		length[i] = 0;
		for(int j=0;j<i;j++)if(list[i]>list[j] && length[i]<length[j]+1)length[i] = length[j] + 1;
	}
}

void change(double *list, int n)		//把数组反过来
{
	for (int i = 1, j = n; i < j; i++, j--)
	{
		double s = list[i] + list[j];
		list[i] = s - list[i];
		list[j] = s - list[j];
	}
}

int main()
{
	int n;
	cout << "输入n和n个正数\n";
	cin >> n;
	double *list = new double[n + 1];
	list[0] = 0;
	for (int i = 1; i <= n; i++)cin >> list[i];
	int *length1 = new int[n + 1], *length2 = new int[n + 1];
	getMaxLength(n, list, length1);
	change(list, n);
	getMaxLength(n, list, length2);
	int lengthmax = 0;		//最大长度
	int index = 0;		//最大长度对应的最大数的下标
	for (int i = 1; i <= n; i++)
	{
		if (length1[i] + length2[n + 1 - i] > lengthmax)
		{
			lengthmax = length1[i] + length2[n + 1 - i];
			index = i;
		}
	}
	cout << "合唱队形最大长度为" << lengthmax-1 << "\n最高者是第" <<index<<"个,高"<< list[index+1];
	system("pause>nul");
	return 0;
}

三,最大子段和、最大子段积

HDU 1003、NBUT 1090 Max Sum

题目:

Description

Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14. 

Input

The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000). 

Output

For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases. 

Sample Input

2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7 -5

Sample Output

Case 1:
14 1 4
 
Case 2:
7 1 6

这个题目就是求最大子段和。

list就是每个数,start[i] 是以 i 结尾的最大字段和的开始下标。

和一般的说法的略有区别的地方是,它要输出最左边的子段。

这就有2个地方要注意,第一,以 i 结尾的最大字段和的开始下标可能有多个,start[i] 必须取最左边的那个。

第二,即使在“start[i] 必须取最左边的那个”这个前提下,最大字段和还是可能有很多个,这时是需要取最左边的。

代码:

#include<iostream>
using namespace std;
 
int list[100001];
int start[100001];
 
int main()
{
	int cas;
	int n;
	cin >> cas;
	for (int i = 1; i <= cas;i++)
	{
		cin >> n;
		list[0] = -1;
		start[1] = 1;
		for (int i = 1; i <= n; i++)
		{
			cin >> list[i];
			if (list[i - 1] >= 0)
			{
				list[i] += list[i - 1];
				start[i] = start[i - 1];
			}
			else start[i] = i;
		}
		int maxs = list[1], key = 1;
		for (int i = 2; i <= n; i++)
		{
			if (maxs < list[i])
			{
				maxs = list[i];
				key = i;
			}
		}
		cout << "Case " << i << ":" << endl << maxs << " " << start[key] << " " << key << endl;
		if (i < cas)cout << endl;
	}
	return 0;
}

力扣 剑指 Offer 42. 连续子数组的最大和

题目:

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

代码:

class Solution {
public:
	int maxSubArray(vector<int>& nums) {
		int n = nums.size();
		if (n == 0)return 0;
		int maxs = nums[0];
		for (int i = 1; i < n; i++)
		{
			if (nums[i - 1] > 0)nums[i] += nums[i - 1];
			maxs = max(maxs, nums[i]);
		}
		return maxs;
	}
};

力扣 152. 乘积最大子序列

题目:

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。

示例 1:

输入: [2,3,-2,4]
输出: 6 
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

思路:

和最大子段和差不多,只要不停更新以当前元素为结尾的所有子段中的最大乘积和最小乘积即可。

代码:

class Solution {
public:
	int maxProduct(vector<int>& nums) {
		if (nums.empty())return 0;
		int ans = nums[0], maxans = nums[0], minans = nums[0], tmp;
		for (int i = 1; i < nums.size(); i++)
		{
			if (nums[i]>0)maxans = max(maxans*nums[i], nums[i]), minans = min(minans*nums[i], nums[i]);
			else tmp = min(maxans*nums[i], nums[i]), maxans = max(minans*nums[i], nums[i]), minans = tmp;
			ans = max(ans, maxans);
		}
		return ans;
	}
};

力扣 1186. 删除一次得到子数组最大和

给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。

注意,删除一个元素后,子数组 不能为空。

示例 1:

输入:arr = [1,-2,0,3]
输出:4
解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2,这样得到 [1, 0, 3],和最大。
示例 2:

输入:arr = [1,-2,-2,3]
输出:3
解释:我们直接选出 [3],这就是最大和。
示例 3:

输入:arr = [-1,-1,-1,-1]
输出:-1
解释:最后得到的子数组不能为空,所以我们不能选择 [-1] 并从中删去 -1 来得到 0。
     我们应该直接选择 [-1],或者选择 [-1, -1] 再从中删去一个 -1。
 

提示:

1 <= arr.length <= 105
-104 <= arr[i] <= 104

class Solution {
public:
	int maximumSum(vector<int>& arr) {
		vector<int>s1 = MaxSubArrayFromEver(arr);
		vector<int>s2 = MaxSubArrayFromEver(Frev(arr));
		int ans = arr[0];
		for (auto x : s1)ans = max(ans, x);
		for (int i = 1; i < s1.size() - 1; i++){
            if(ans<s1[i + 1] + s2[s2.size() - i]){
                ans=s1[i + 1] + s2[s2.size() - i];
            }
        }
		return ans;
	}
};

四,(最长)公共子序列

HDU 5791 Two(公共子序列的数目)

题目:

Description

Alice gets two sequences A and B. A easy problem comes. How many pair of sequence A' and sequence B' are same. For example, {1,2} and {1,2} are same. {1,2,4} and {1,4,2} are not same. A' is a subsequence of A. B' is a subsequence of B. The subsequnce can be not continuous. For example, {1,1,2} has 7 subsequences {1},{1},{2},{1,1},{1,2},{1,2},{1,1,2}. The answer can be very large. Output the answer mod 1000000007.

Input

The input contains multiple test cases. 

For each test case, the first line cantains two integers                . The next line contains N integers. The next line followed M integers. All integers are between 1 and 1000.

Output

For each test case, output the answer mod 1000000007.

Sample Input

3 2
1 2 3
2 1
3 2
1 2 3
1 2

Sample Output

2
3

这个题目,n个数我用list存起来,还有m个数都用变量k过一遍,不需要存。

同时,结果也只需要1个1维数组r就可以存起来。

这就很像0-1背包问题了,因为递推式的特性,只需要对r反复的刷就可以了。

代码:

#include<iostream>
#include<string.h>
using namespace std;
 
int n, m;
int list[1001];
int k;
int r[1001];
 
int main()
{
	while (cin >> n >> m)
	{
		for (int i = 1; i <= n; i++)cin >> list[i];
		cin >> k;
		r[0] = 1;
		for (int i = 1; i <= n; i++)
		{
			r[i] = r[i - 1];
			if (list[i] == k)r[i]++;
		}
		for (int i = 1; i < m; i++)
		{
			cin >>k;
			for (int j = n; j >0; j--)if (list[j] == k)
			for (int jj = j; jj <= n; jj++)r[jj] = (r[jj] + r[j - 1]) % 1000000007;
		}
		cout << r[n] - 1 << endl;
	}	
	return 0;
}

这个代码,里面有三重循环,一般人做这个题目应该都是二重循环吧(难怪那么快)

主要是我一开始就准备只用一维数组来表示结果,导致我选择的递推式有点复杂。

POJ 1458 Common Subsequence(最长公共子序列)

题目:

Description

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, x  ij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output

For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input

abcfbc         abfcab
programming    contest 
abcd           mnp

Sample Output

4
2
0

这个题目我总是超时,后来在同学的帮助之下才知道,因为动态规划里面判断是否计算过出了问题。

错误的代码:

#include<stdio.h>
#include <stdlib.h> 
#include<string.h>
 
 
char s1[1000];
char s2[1000];
int **list;
 
int f(int m, int n)
{
	if (m < 0 || n < 0)return 0;
	if (list[m][n])return list[m][n];
	if (s1[m] == s2[n])
	{
		list[m][n] = f(m - 1, n - 1) + 1;
		return list[m][n];
	}
	int a = f(m, n - 1);
	int b = f(m - 1, n);
	list[m][n] = (a>b) ? a : b;
	return list[m][n];
}
 
int main()
{
	while (scanf("%s%s", s1,s2)!=-1)
	{
		int a = strlen(s1);
		int b = strlen(s2);
		list = new int*[a];
		for (int i = 0; i < a; i++)
		{
			list[i] = new int[b];
			memset(list[i], 0, b * 4);
		}
		printf("%d\n", f(a - 1, b - 1));
	}
	return 0;
}

因为list初始化的值为0,函数f计算之后给list重新赋值也可能为0,所以就出了问题。

正确的代码:

#include<stdio.h>
#include <stdlib.h> 
#include<string.h>
 
 
char s1[1000];
char s2[1000];
int **list;
 
int f(int m, int n)
{
	if (m < 0 || n < 0)return 0;
	if (list[m][n]!=-1)return list[m][n];
	if (s1[m] == s2[n])
	{
		list[m][n] = f(m - 1, n - 1) + 1;
		return list[m][n];
	}
	int a = f(m, n - 1);
	int b = f(m - 1, n);
	list[m][n] = (a>b) ? a : b;
	return list[m][n];
}
 
int main()
{
	while (scanf("%s%s", s1,s2)!=-1)
	{
		int a = strlen(s1);
		int b = strlen(s2);
		list = new int*[a];
		for (int i = 0; i < a; i++)
		{
			list[i] = new int[b];
			memset(list[i], -1, b * 4);
		}
		printf("%d\n", f(a - 1, b - 1));
	}
	return 0;
}

CSU 1060 Nearest Sequence (三个数组的最长公共子序列)

题目:

Description

        Do you remember the "Nearest Numbers"? Now here comes its brother:"Nearest Sequence".Given three sequences of char,tell me the length of the longest common subsequence of the three sequences.

Input

        There are several test cases.For each test case,the first line gives you the first sequence,the second line gives you the second one and the third line gives you the third one.(the max length of each sequence is 100)

Output

        For each test case,print only one integer :the length of the longest common subsequence of the three sequences.

Sample Input

abcd
abdc
dbca
abcd
cabd
tsc

Sample Output

2
1

代码:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
 
char a[101], b[101], c[101];
int la, lb, lc;
int ans[101][101][101];
 
int dp(int ka, int kb, int kc)
{
	if (ka < 0 || kb < 0 || kc < 0)return 0;
	if (ans[ka][kb][kc] > -1)return ans[ka][kb][kc];
	ans[ka][kb][kc] = max(max(dp(ka - 1, kb, kc), dp(ka, kb - 1, kc)), dp(ka, kb, kc - 1));
	if (a[ka] == b[kb] && b[kb] == c[kc])ans[ka][kb][kc] = dp(ka - 1, kb - 1, kc - 1) + 1;
	return ans[ka][kb][kc];
}
 
int main()
{
	while (cin >> a >> b >> c)
	{
		memset(ans, -1, sizeof(ans));
		cout << dp(strlen(a) - 1, strlen(b) - 1, strlen(c) - 1) << endl;
	}
	return 0;
}

力扣 1143. 最长公共子序列

题目:

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。
 

提示:

1 <= text1.length <= 1000
1 <= text2.length <= 1000
输入的字符串只含有小写英文字符。

代码:

string s1, s2;
int list_[1005][1005];
 
int f(int m, int n)
{
	if (m < 0 || n < 0)return 0;
	if (list_[m][n] != -1)return list_[m][n];
	if (s1[m] == s2[n])
	{
		list_[m][n] = f(m - 1, n - 1) + 1;
		return list_[m][n];
	}
	int a = f(m, n - 1);
	int b = f(m - 1, n);
	list_[m][n] = (a>b) ? a : b;
	return list_[m][n];
}
 
class Solution {
public:
	int longestCommonSubsequence(string text1, string text2) {	
		int a = text1.length();
		int b = text2.length();
		s1 = text1, s2 = text2;
		for (int i = 0; i < a; i++)
		{
			memset(list_[i], -1, b * 4);
		}
		return f(a - 1, b - 1);
	}
};

五,超递增数列

CSU 1203 Super-increasing sequence

题目:

Description

如果一个序列中任意一项都大于前面所有项之和,那么我们就称这个序列为超递增序列。

现在有一个整数序列,你可以将序列中任意相邻的若干项合并成一项,合并之后这项的值为合并前各项的值之和。通过若干次合并,最终一定能得到一个超递增序列,那么得到的超递增序列最多能有多少项呢?

Input

输入数据的第一行包含正整数T (1 <= T <= 500),表示接下来一共有T组测试数据。

每组测试数据的第一行包含一个整数N (1 <= N <= 100000),表示这个整数序列一共有N项。接下来一行包含N个不大于10000的正整数,依次描述了这个序列中各项的值。

至多有10组数据满足N > 1000。

Output

对于每组测试数据,用一行输出一个整数,表示最终得到的超递增序列最多能有多少项。

Sample Input

3
2
1 1
3
1 2 4
6
1 2 4 3 6 5

Sample Output

1
3
4

代码:

#include<iostream>
using namespace std;
 
int main()
{
    int T;
    cin >> T;
    int n;
    while (T--)
    {
        cin >> n;
        int *a = new int[n + 1];
        int *s = new int[n + 1];    
        s[0] = 0;
        int *f = new int[n + 1];
        f[0] = 0;
        f[1] = 1;
        int c[30];      //c[i]是 使得 f(x)=i的最小x
        c[1] = 1;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
            s[i] = s[i - 1] + a[i];
            if (i > 1)
            {
                if (s[i] - s[c[f[i - 1]]] - s[c[f[i - 1]]] > 0)
                {
                    f[i] = f[i - 1] + 1;
                    c[f[i]] = i;
                }
                else f[i] = f[i - 1];
            }
        }
        cout << f[n] << endl;
    }
    return 0;
}

六,超最大字段和

HDU 1244 Max Sum Plus Plus Plus

题目:

Description

给定一个由n个正整数组成的整数序列 

a1 a2 a3 ... an 

求按先后次序在其中取m段长度分别为l1、l2、l3...lm的不交叠的连续整数的和的最大值。 

Input

第一行是一个整数n(0 ≤ n ≤ 1000),n = 0表示输入结束 
第二行的第一个数是m(1 ≤ m ≤ 20), 
第二行接下来有m个整数l1,l2...lm。 
第三行是n个整数a1, a2, a2 ... an. 

Output

输出m段整数和的最大值。 

Sample Input

3
2 1 1
1 2 3
4
2 1 2
1 2 3 5
0

Sample Output

5
10

首先,很明显是动态规划。

这个题目可以说是HDU 1003 NBUT 1090 Max Sum的升级,思路差不多。

因为我的代码里面用到了空间压缩,没有用二维数组,所以代码略显复杂。

代码:

#include<iostream>
using namespace std;

int n, m;
int list[1001], sum[1001], maxx[1001], len[20], sumlen[20];

int main()
{
	while (cin >> n)
	{
		if (n == 0)break;
		cin >> m;
		for (int i = 0; i < m; i++)
		{
			cin >> len[i];
			sumlen[i] = len[i];
			if (i)sumlen[i] += sumlen[i - 1];
		}
		for (int i = 0; i < n; i++)
		{
			cin >> list[i];
			sum[i] = list[i];
			if (i)sum[i] += sum[i - 1];
		}
		for (int i = 0; i < n; i++)maxx[i] = 0;
		maxx[0] = sum[len[0] - 1];
		for (int j = 1; j <= n - len[0]; j++)
		{
			maxx[j] = sum[j + len[0] - 1] - sum[j - 1];
			if (maxx[j] < maxx[j - 1])maxx[j] = maxx[j - 1];
		}
		for (int i = 1; i < m; i++)
		{
			for (int j = n - len[i]; j >= sumlen[i - 1]; j--)
			maxx[j] = sum[j + len[i] - 1] - sum[j - 1] + maxx[j - len[i - 1]];
			for (int j = 1; j <= n - len[i]; j++)
			if (maxx[j] < maxx[j - 1])maxx[j] = maxx[j - 1];
		}
		if (n >= len[m - 1])cout << maxx[n - len[m - 1]] << endl;
		else cout << 0 << endl;
	}
	return 0;
}

七,else

力扣 1218. 最长定差子序列

给你一个整数数组 arr 和一个整数 difference,请你找出 arr 中所有相邻元素之间的差等于给定 difference 的等差子序列,并返回其中最长的等差子序列的长度。

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。
示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。
示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。
 

提示:

1 <= arr.length <= 10^5
-10^4 <= arr[i], difference <= 10^4

如果只需要O(n^2)的算法,那就太简单了,那就和 力扣 OJ 300. 最长上升子序列 没有什么区别,

直接微改得到的代码是这样的:

class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) {
        map<int, int>ans;
		ans[0] = 1;
		for (int i = 1; i < arr.size(); i++)
		{
			ans[i] = 1;
			for (int j = 0; j < i; j++)
				if (arr[j]+difference == arr[i] && ans[i] < ans[j] + 1)ans[i] = ans[j] + 1;
		}
		int m = 0;
		for (int i = 0; i < arr.size(); i++)if (m < ans[i])m = ans[i];
		return m;
    }
};

果然超时。

这个题目明显需要O(n)的算法,

思路还是一样,只需要基于数据结构进行时间优化即可

class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) {
        map<int, int>ans;
        int res=0;
		for (int i = 0; i < arr.size(); i++)
		{
            ans[arr[i]]=max(ans[arr[i]],ans[arr[i]-difference]+1);
            res=max(res,ans[arr[i]]);
		}
		return res;
    }
};

力扣 1458. 两个子序列的最大点积

给你两个数组 nums1 和 nums2 。

请你返回 nums1 和 nums2 中两个长度相同的 非空 子序列的最大点积。

数组的非空子序列是通过删除原数组中某些元素(可能一个也不删除)后剩余数字组成的序列,但不能改变数字间相对顺序。比方说,[2,3,5] 是 [1,2,3,4,5] 的一个子序列而 [1,5,3] 不是。

示例 1:

输入:nums1 = [2,1,-2,5], nums2 = [3,0,-6]
输出:18
解释:从 nums1 中得到子序列 [2,-2] ,从 nums2 中得到子序列 [3,-6] 。
它们的点积为 (2*3 + (-2)*(-6)) = 18 。
示例 2:

输入:nums1 = [3,-2], nums2 = [2,-6,7]
输出:21
解释:从 nums1 中得到子序列 [3] ,从 nums2 中得到子序列 [7] 。
它们的点积为 (3*7) = 21 。
示例 3:

输入:nums1 = [-1,-1], nums2 = [1,1]
输出:-1
解释:从 nums1 中得到子序列 [-1] ,从 nums2 中得到子序列 [1] 。
它们的点积为 -1 。
 

提示:

1 <= nums1.length, nums2.length <= 500
-1000 <= nums1[i], nums2[i] <= 100

int ans[501][501],minn=-100005;
class Solution {
public:
    int dp(vector<int>& nums1, vector<int>& nums2,int k1,int k2)
    {
        if(ans[k1][k2]>minn)return ans[k1][k2];
        ans[k1][k2]=nums1[k1]*nums2[k2];
        if(k1>0)ans[k1][k2]=max(dp(nums1,nums2,k1-1,k2),ans[k1][k2]);
        if(k2>0)ans[k1][k2]=max(dp(nums1,nums2,k1,k2-1),ans[k1][k2]);
        if(k1>0 && k2>0)ans[k1][k2]=max(dp(nums1,nums2,k1-1,k2-1)+nums1[k1]*nums2[k2],ans[k1][k2]);
        return ans[k1][k2];
    }
    int maxDotProduct(vector<int>& nums1, vector<int>& nums2) {
        int len1=nums1.size(),len2=nums2.size();
        for(int i=0;i<len1;i++)for(int j=0;j<len2;j++)ans[i][j]=minn;
        return dp(nums1,nums2,len1-1,len2-1);
    }
};

力扣 剑指 Offer II 091. 粉刷房子

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。

当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。

例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。

请计算出粉刷完所有房子最少的花费成本。

示例 1:

输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色
     最少花费: 2 + 5 + 3 = 10。

示例 2:

输入: costs = [[7,6,2]]
输出: 2

提示:

  • costs.length == n
  • costs[i].length == 3
  • 1 <= n <= 100
  • 1 <= costs[i][j] <= 20
class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        vector<int> ans = costs[0];
        for(int i=1;i<costs.size();i++){
            vector<int>t = costs[i];
            t[0]+=min(ans[1],ans[2]);
            t[1]+=min(ans[0],ans[2]);
            t[2]+=min(ans[1],ans[0]);
            ans=t;
        }
        return min(ans[0],min(ans[1],ans[2]));
    }
};

力扣 276. 栅栏涂色

有 k 种颜色的涂料和一个包含 n 个栅栏柱的栅栏,请你按下述规则为栅栏设计涂色方案:

每个栅栏柱可以用其中 一种 颜色进行上色。
相邻的栅栏柱 最多连续两个 颜色相同。
给你两个整数 k 和 n ,返回所有有效的涂色 方案数 。

示例 1:


输入:n = 3, k = 2
输出:6
解释:所有的可能涂色方案如上图所示。注意,全涂红或者全涂绿的方案属于无效方案,因为相邻的栅栏柱 最多连续两个 颜色相同。
示例 2:

输入:n = 1, k = 1
输出:1
示例 3:

输入:n = 7, k = 2
输出:42
 

提示:

1 <= n <= 50
1 <= k <= 105
题目数据保证:对于输入的 n 和 k ,其答案在范围 [0, 231 - 1] 内

class Solution {
public:
	int numWays(int n, int k) {
		map<int, long long>ans;
		ans[0] = 1, ans[1] = k, ans[2] = k * k, ans[3] = ans[2] *k - k;
		for (int i = 4; i <= n; i++)ans[i] = ans[i - 1]*k - ans[i - 3]*(k-1);
		return ans[n];
	}
};

力扣 368. 最大整除子集

给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:

  • answer[i] % answer[j] == 0 ,或
  • answer[j] % answer[i] == 0

如果存在多个有效解子集,返回其中任何一个均可。

示例 1:

输入:nums = [1,2,3]
输出:[1,2]
解释:[1,3] 也会被视为正确答案。

示例 2:

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

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 2 * 109
  • nums 中的所有整数 互不相同
class Solution {
public:
	vector<int> largestDivisibleSubset(vector<int>& nums) {
		sort(nums.begin(), nums.end());
		map<int, int>pre, len;
		int maxLen = 0, id = 0;
		for (int i = 1; i < nums.size(); i++) {
			for (int j = 0; j < i; j++) {
				if (len[i] < len[j] + 1 && nums[i] % nums[j] == 0)pre[i] = j+1, len[i] = len[j] + 1;
			}
			if (maxLen < len[i])maxLen = len[i], id = i;
		}
		vector<int>ans;
		while(id>=0){
            ans.push_back(nums[id]);
            id=pre[id]-1;
        }
		return ans;
	}
};

力扣 1911. 最大子序列交替和

一个下标从 0 开始的数组的 交替和 定义为 偶数 下标处元素之  减去 奇数 下标处元素之  。

  • 比方说,数组 [4,2,5,3] 的交替和为 (4 + 5) - (2 + 3) = 4 。

给你一个数组 nums ,请你返回 nums 中任意子序列的 最大交替和 (子序列的下标 重新 从 0 开始编号)。

一个数组的 子序列 是从原数组中删除一些元素后(也可能一个也不删除)剩余元素不改变顺序组成的数组。比方说,[2,7,4] 是 [4,2,3,7,2,1,4] 的一个子序列(加粗元素),但是 [2,4,2] 不是。

示例 1:

输入:nums = [4,2,5,3]
输出:7
解释:最优子序列为 [4,2,5] ,交替和为 (4 + 5) - 2 = 7 。

示例 2:

输入:nums = [5,6,7,8]
输出:8
解释:最优子序列为 [8] ,交替和为 8 。

示例 3:

输入:nums = [6,2,1,2,4,5]
输出:10
解释:最优子序列为 [6,1,5] ,交替和为 (6 + 5) - 1 = 10 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105
class Solution {
public:
	long long maxAlternatingSum(vector<int>& nums) {
		long long smax = 0, smin = 0;
		for (int i = nums.size() - 1; i >= 0; i--) {
			long long smax2 = nums[i] - smin, smin2 = nums[i] - smax;
			smax = max(smax, smax2), smin = min(smin, smin2);
		}
		return smax;
	}
};

力扣 2140. 解决智力问题

给你一个下标从 0 开始的二维整数数组 questions ,其中 questions[i] = [pointsi, brainpoweri] 。

这个数组表示一场考试里的一系列题目,你需要 按顺序 (也就是从问题 0 开始依次解决),针对每个问题选择 解决 或者 跳过 操作。解决问题 i 将让你 获得  pointsi 的分数,但是你将 无法 解决接下来的 brainpoweri 个问题(即只能跳过接下来的 brainpoweri 个问题)。如果你跳过问题 i ,你可以对下一个问题决定使用哪种操作。

  • 比方说,给你 questions = [[3, 2], [4, 3], [4, 4], [2, 5]] :
    • 如果问题 0 被解决了, 那么你可以获得 3 分,但你不能解决问题 1 和 2 。
    • 如果你跳过问题 0 ,且解决问题 1 ,你将获得 4 分但是不能解决问题 2 和 3 。

请你返回这场考试里你能获得的 最高 分数。

示例 1:

输入:questions = [[3,2],[4,3],[4,4],[2,5]]
输出:5
解释:解决问题 0 和 3 得到最高分。
- 解决问题 0 :获得 3 分,但接下来 2 个问题都不能解决。
- 不能解决问题 1 和 2
- 解决问题 3 :获得 2 分
总得分为:3 + 2 = 5 。没有别的办法获得 5 分或者多于 5 分。

示例 2:

输入:questions = [[1,1],[2,2],[3,3],[4,4],[5,5]]
输出:7
解释:解决问题 1 和 4 得到最高分。
- 跳过问题 0
- 解决问题 1 :获得 2 分,但接下来 2 个问题都不能解决。
- 不能解决问题 2 和 3
- 解决问题 4 :获得 5 分
总得分为:2 + 5 = 7 。没有别的办法获得 7 分或者多于 7 分。

提示:

  • 1 <= questions.length <= 105
  • questions[i].length == 2
  • 1 <= pointsi, brainpoweri <= 105
class Solution {
public:
	long long mostPoints(vector<vector<int>>& questions) {
		m.clear();
		return  mostPoints(0, questions);
	}
	long long mostPoints(int k, vector<vector<int>>& questions)
	{
		if (k >= questions.size())return 0;
		if (m[k])return m[k];
		return m[k] = max(questions[k][0] + mostPoints(k + 1 + questions[k][1], questions), mostPoints(k + 1, questions));
	}
	map<int, long long>m;
};

力扣 2327. 知道秘密的人数

在第 1 天,有一个人发现了一个秘密。

给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后,每天 给一个新的人 分享 秘密。同时给你一个整数 forget ,表示每个人在发现秘密 forget 天之后会 忘记 这个秘密。一个人 不能 在忘记秘密那一天及之后的日子里分享秘密。

给你一个整数 n ,请你返回在第 n 天结束时,知道秘密的人数。由于答案可能会很大,请你将结果对 109 + 7 取余 后返回。

示例 1:

输入:n = 6, delay = 2, forget = 4
输出:5
解释:
第 1 天:假设第一个人叫 A 。(一个人知道秘密)
第 2 天:A 是唯一一个知道秘密的人。(一个人知道秘密)
第 3 天:A 把秘密分享给 B 。(两个人知道秘密)
第 4 天:A 把秘密分享给一个新的人 C 。(三个人知道秘密)
第 5 天:A 忘记了秘密,B 把秘密分享给一个新的人 D 。(三个人知道秘密)
第 6 天:B 把秘密分享给 E,C 把秘密分享给 F 。(五个人知道秘密)

示例 2:

输入:n = 4, delay = 1, forget = 3
输出:6
解释:
第 1 天:第一个知道秘密的人为 A 。(一个人知道秘密)
第 2 天:A 把秘密分享给 B 。(两个人知道秘密)
第 3 天:A 和 B 把秘密分享给 2 个新的人 C 和 D 。(四个人知道秘密)
第 4 天:A 忘记了秘密,B、C、D 分别分享给 3 个新的人。(六个人知道秘密)

提示:

  • 2 <= n <= 1000
  • 1 <= delay < forget <= n

先把每一天有多少人是新增的给求出来。

然后再去求每一天有多少人。

class Solution {
public:
    int peopleAwareOfSecret(int n, int d, int forget) {
        map<int,long long>m1,m2;
        m1[1]=1,m1[2]=d==1?1:0;
        for(int i=2;i<n;i++)m1[i+1]=(m1[i]+m1[i-d+1]-m1[i-forget+1])%1000000007;
        m2[1]=1;
        for(int i=2;i<=n;i++)m2[i]=(m2[i-1]+m1[i]-m1[i-forget])%1000000007;
        return (m2[n]+1000000007)%1000000007;
    }
};

力扣 651. 四个键的键盘

假设你有一个特殊的键盘包含下面的按键:

  • A:在屏幕上打印一个 'A'
  • Ctrl-A:选中整个屏幕。
  • Ctrl-C:复制选中区域到缓冲区。
  • Ctrl-V:将缓冲区内容输出到上次输入的结束位置,并显示在屏幕上。

现在,你可以 最多 按键 n 次(使用上述四种按键),返回屏幕上最多可以显示 'A' 的个数 

示例 1:

输入: n = 3
输出: 3
解释: 
我们最多可以在屏幕上显示三个'A'通过如下顺序按键:
A, A, A

示例 2:

输入: n = 7
输出: 9
解释: 
我们最多可以在屏幕上显示九个'A'通过如下顺序按键:
A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V

提示:

  • 1 <= n <= 50
class Solution {
public:
    int maxA(int n) {
        vector<int>v(51);
        for(int i=1;i<=n;i++){
            v[i]=v[i-1]+1;
            for(int k=3;k<i;k++)v[i]=max(v[i],v[i-k]*(k-1));
        }
        return v[n];
    }
};

力扣 1259. 不相交的握手

偶数 个人站成一个圆,总人数为 num_people 。每个人与除自己外的一个人握手,所以总共会有 num_people / 2 次握手。

将握手的人之间连线,请你返回连线不会相交的握手方案数。

由于结果可能会很大,请你返回答案  10^9+7 后的结果。

示例 1:

输入:num_people = 2
输出:1

示例 2:

输入:num_people = 4
输出:2
解释:总共有两种方案,第一种方案是 [(1,2),(3,4)] ,第二种方案是 [(2,3),(4,1)] 。

示例 3:

输入:num_people = 6
输出:5

示例 4:

输入:num_people = 8
输出:14

提示:

  • 2 <= num_people <= 1000
  • num_people % 2 == 0
class Solution {
public:
    int numberOfWays(int numPeople) {
        vector<long long>v(1001);
        v[0]=1;
        for(int i=1;i<=numPeople;i++){
            for(int j=0;j<=i-2;j++)v[i]=(v[i]+v[j]*v[i-2-j])%1000000007;
        }
        return v[numPeople];
    }
};

力扣 198. 打家劫舍

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

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

示例 1:

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

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400
class Solution {
public:
    int rob(vector<int>& nums) {
        map<int,int>m;
        for(int i=0;i<nums.size();i++){
            m[i]=max(m[i-2],m[i-3])+nums[i];
        }
        return max(m[nums.size()-1],m[nums.size()-2]);
    }
};

力扣 2369. 检查数组是否存在有效划分

给你一个下标从 0 开始的整数数组 nums ,你必须将数组划分为一个或多个 连续 子数组。

如果获得的这些子数组中每个都能满足下述条件 之一 ,则可以称其为数组的一种 有效 划分:

  1. 子数组  由 2 个相等元素组成,例如,子数组 [2,2] 。
  2. 子数组  由 3 个相等元素组成,例如,子数组 [4,4,4] 。
  3. 子数组  由 3 个连续递增元素组成,并且相邻元素之间的差值为 1 。例如,子数组 [3,4,5] ,但是子数组 [1,3,5] 不符合要求。

如果数组 至少 存在一种有效划分,返回 true ,否则,返回 false 。

示例 1:

输入:nums = [4,4,4,5,6]
输出:true
解释:数组可以划分成子数组 [4,4] 和 [4,5,6] 。
这是一种有效划分,所以返回 true 。

示例 2:

输入:nums = [1,1,1,2]
输出:false
解释:该数组不存在有效划分。

提示:

  • 2 <= nums.length <= 105
  • 1 <= nums[i] <= 106
class Solution {
public:
	bool validPartition(vector<int>& nums) {
		map<int, bool>m;
		m[-1] = true;
		m[1] = nums[1] == nums[0];
		for (int i = 2; i < nums.size(); i++) {
			m[i] = nums[i] == nums[i - 1] + 1 && nums[i] == nums[i - 2] + 2 && m[i - 3];
			if (nums[i] == nums[i - 1]) {
				m[i] = m[i] || m[i - 2] || (nums[i] == nums[i - 2] && m[i - 3]);
			}
		}
		return m[nums.size() - 1];
	}
};

力扣 1997. 访问完所有房间的第一天

你需要访问 n 个房间,房间从 0 到 n - 1 编号。同时,每一天都有一个日期编号,从 0 开始,依天数递增。你每天都会访问一个房间。

最开始的第 0 天,你访问 0 号房间。给你一个长度为 n 且 下标从 0 开始 的数组 nextVisit 。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:

  • 假设某一天,你访问 i 号房间。
  • 如果算上本次访问,访问 i 号房间的次数为 奇数 ,那么 第二天 需要访问 nextVisit[i] 所指定的房间,其中 0 <= nextVisit[i] <= i 。
  • 如果算上本次访问,访问 i 号房间的次数为 偶数 ,那么 第二天 需要访问 (i + 1) mod n 号房间。

请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对 109 + 7 取余后的结果。

示例 1:

输入:nextVisit = [0,0]
输出:2
解释:
- 第 0 天,你访问房间 0 。访问 0 号房间的总次数为 1 ,次数为奇数。
  下一天你需要访问房间的编号是 nextVisit[0] = 0
- 第 1 天,你访问房间 0 。访问 0 号房间的总次数为 2 ,次数为偶数。
  下一天你需要访问房间的编号是 (0 + 1) mod 2 = 1
- 第 2 天,你访问房间 1 。这是你第一次完成访问所有房间的那天。

示例 2:

输入:nextVisit = [0,0,2]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,0,0,1,2,...] 。
第 6 天是你访问完所有房间的第一天。

示例 3:

输入:nextVisit = [0,1,2,0]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,1,2,2,3,...] 。
第 6 天是你访问完所有房间的第一天。

提示:

  • n == nextVisit.length
  • 2 <= n <= 105
  • 0 <= nextVisit[i] <= i
class Solution {
public:
	int firstDayBeenInAllRooms(vector<int>& nextVisit) {
		vector<int>ans(nextVisit.size());
		ans[0] = 0;
		for (int i = 1; i < ans.size(); i++) {
			ans[i] = (ans[i - 1] * 2 % 1000000007 + 1000000009- ans[nextVisit[i - 1]]) % 1000000007;
		}
		return ans[ans.size() - 1];
	}
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值