统计整型序列中有多少个子序列可以被拆成一个不严格递增子子序列和一个不严格递减子子序列

最近看到一道题目:统计整型序列中有多少个子序列可以被拆成一个不严格递增子子序列和一个不严格递减子子序列,其中不严格递增子子序列至少含有 1 个元素,不严格递减子子序列可以为空(长度不限)。

换个说法:统计整型序列中有多少个子序列可以被拆成一个非(严格)递减子子序列和一个非(严格)递增子子序列,其中非(严格)递减子子序列至少含有 1 个元素,非(严格)递增子子序列可以为空(长度不限)。

注意:一个元素的序列同时可以被认为是递增或是递减的。
子序列的定义相信大家都比较清楚,大概就是在序列中提取出至少一个元素组成的一个新的序列使得新的序列中的元素位置顺序与原序列的元素位置顺序不变,每一个序列都是序列本身的子序列。
拆分可能是比较难理解的,无法言传只能意会,直接给一些拆分示例:

{2, 3, 1} -> {2, 3} 和 {1}
{4, 5, 2, 6, 1} -> {4, 5, 6} 和 {2, 1} (当然拆分方式不唯一)
{2, 1, 4, 3} -> 无法拆分

首先最简单的思路,就是遍历这个序列中的所有子序列,然后找出子序列中的最长递增子序列,判断剩余的是不是递减即可。在判断剩余是否递减的时候做了个优化,即不保存剩余序列,直接从 lastValue = INF 开始每次更新最小值,如果下一个值高于 lastValue 就立即 return false,判断结束就 return true。

上代码:

#include <iostream>
#include <string>
#include <vector>
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EOF
#define EOF (-1)
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef INF
#define INF 0x7FFFFFFF
#endif
using namespace std;


/* handle input string */
vector<int> parseLine(const string& _line, char sep)
{
	vector<int> v;
	string line = "";
	for (int i = 0; i < _line.size(); ++i)
		if ((_line[i] >= '0' && _line[i] <= '9') || (_line[i] == sep && (line.size() > 0 && line[line.size() - 1] != sep)))
			line += _line[i];
	if (line.size() <= 0)
		return v;
	while (line[line.size() - 1] == sep)
		line = line.substr(0, line.size() - 1);
	if (line.size() <= 0)
		return v;
	size_t pos = 0, pos2 = 0, len = line.length();
	while (pos < len)
	{
		pos2 = line.find(sep, pos);
		if (pos2 == -1)
			pos2 = len;
		v.push_back(atoi(line.substr(pos, pos2 - pos).c_str()));
		pos = pos2 + 1;
	}
	return v;
}


/* handle problem LIS */
vector<int> getdp2(vector<int>& arr)
{
	vector<int> dp(arr.size(), 0);
	vector<int> ends(arr.size(), 0);
	ends[0] = arr[0];
	dp[0] = 1;
	int right = 0, l = 0, r = 0, m = 0;
	for (int i = 1; i < arr.size(); ++i)
	{
		l = 0;
		r = right;
		while (l <= r)
		{
			m = (l + r) / 2;
			if (arr[i] > ends[m])
				l = m + 1;
			else
				r = m - 1;
		}
		right = max(right, l);
		ends[l] = arr[i];
		dp[i] = l + 1;
	}
	return dp;
}

vector<int> generateLIS(vector<int>& arr, vector<int>& dp)
{
	int len = 0, index = 0;
	for (int i = 0; i < dp.size(); ++i)
		if (dp[i] > len)
		{
			len = dp[i];
			index = i;
		}
	vector<int> lis(len, 0);
	lis[--len] = arr[index];
	for (int i = index; i >= 0; --i)
		if (arr[i] < arr[index] && dp[i] == dp[index] - 1) // find subarray from back to front
		{
			lis[--len] = arr[i];
			index = i;
		}
	return lis;
}


/* Handle current problem */
bool checkDecreasing(vector<int>& arr, vector<int>& increasingArray)
{
	int lastRemain = INF;
	size_t j = 0;
	for (int i = 0; i < arr.size(); ++i)
		if (j < increasingArray.size() && arr[i] == increasingArray[j])
			++j;
		else if (arr[i] <= lastRemain) // check for decreasing ( = means not severe control)
			lastRemain = arr[i];
		else
			return false;
	return true;
}

bool checkSimple(vector<int>& arr)
{
	vector<int> dp = getdp2(arr);
	vector<int> increasingArray = generateLIS(arr, dp);
	if (increasingArray.size() <= 0)
		return false;
	else if (checkDecreasing(arr, increasingArray))
		return true;
	else
		return false;
}

int countSimple(vector<int>& arr)
{
	int cnt = 0;
	for (int i = 0; i < arr.size(); ++i)
	{
		vector<int> sub_arr;
		for (int j = i; j < arr.size(); ++j)
		{
			sub_arr.push_back(arr[j]);
			if (checkSimple(sub_arr))
				cnt += 1;
		}
	}
	return cnt;
}



int main()
{
	rewind(stdin);
	fflush(stdin);
	int totalCnt = 0;
	cin >> totalCnt;
	if (totalCnt <= 0)
		return EXIT_FAILURE;
	vector<string> line;
	for (int i = 0; i < totalCnt; ++i)
	{
		line.push_back("");
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
	}
	for (int i = 0; i < totalCnt; ++i)
	{
		vector<int> arr = parseLine(line[i], ' ');
		cout << countSimple(arr) << endl;
	}

	return EXIT_SUCCESS;
}

刚好有测试案例,美滋滋:
input:

3
3
2 3 1
6
4 5 2 6 1 3
10
7 10 1 8 3 9 2 4 6 5

output:

6
19
39

其中 input 的第一行(自然语言,不是编程语言的 1)代表总共 3 个测试案例,接下来六行则是 表示该测试样例的个数 和 测试样例 交替。

结果程序输出:

6
18
36

傻眼,说明用最长子序列不能覆盖所有情况啊!那就先遍历全部递增子序列吧!
一波注释并加入二维 vector 后,代码如下:

#include <iostream>
#include <string>
#include <vector>
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EOF
#define EOF (-1)
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef INF
#define INF 0x7FFFFFFF
#endif
using namespace std;


/* handle input string */
vector<int> parseLine(const string& _line, char sep)
{
	vector<int> v;
	string line = "";
	for (int i = 0; i < _line.size(); ++i)
		if ((_line[i] >= '0' && _line[i] <= '9') || (_line[i] == sep && (line.size() > 0 && line[line.size() - 1] != sep)))
			line += _line[i];
	if (line.size() <= 0)
		return v;
	while (line[line.size() - 1] == sep)
		line = line.substr(0, line.size() - 1);
	if (line.size() <= 0)
		return v;
	size_t pos = 0, pos2 = 0, len = line.length();
	while (pos < len)
	{
		pos2 = line.find(sep, pos);
		if (pos2 == -1)
			pos2 = len;
		v.push_back(atoi(line.substr(pos, pos2 - pos).c_str()));
		pos = pos2 + 1;
	}
	return v;
}


/* handle problem LIS */
/*
vector<int> getdp2(vector<int>& arr)
{
	vector<int> dp(arr.size(), 0);
	vector<int> ends(arr.size(), 0);
	ends[0] = arr[0];
	dp[0] = 1;
	int right = 0, l = 0, r = 0, m = 0;
	for (int i = 1; i < arr.size(); ++i)
	{
		l = 0;
		r = right;
		while (l <= r)
		{
			m = (l + r) / 2;
			if (arr[i] > ends[m])
				l = m + 1;
			else
				r = m - 1;
		}
		right = max(right, l);
		ends[l] = arr[i];
		dp[i] = l + 1;
	}
	return dp;
}

vector<int> generateLIS(vector<int>& arr, vector<int>& dp)
{
	int len = 0, index = 0;
	for (int i = 0; i < dp.size(); ++i)
		if (dp[i] > len)
		{
			len = dp[i];
			index = i;
		}
	vector<int> lis(len, 0);
	lis[--len] = arr[index];
	for (int i = index; i >= 0; --i)
		if (arr[i] < arr[index] && dp[i] == dp[index] - 1) // find subarray from back to front
		{
			lis[--len] = arr[i];
			index = i;
		}
	return lis;
}
*/


/* Handle current problem */
void dfs(int x, vector<int>& nums, vector<int>& v, vector<vector<int>>& vs)
{
	if (x == nums.size())
	{
		if (v.size() >= 1) // exit loop
			vs.push_back(v);
		return;
	}
	if (v.size() == 0 || nums[x] >= v[v.size() - 1]) // select ( = means not severe control)
	{
		v.push_back(nums[x]);
		dfs(x + 1, nums, v, vs);
		v.pop_back(); // back
	}
	if (v.size() == 0 || nums[x] != v[v.size() - 1])
		dfs(x + 1, nums, v, vs); // do not select
	return;
}

vector<vector<int>> findIncreasingArray(vector<int>& nums)
{
	vector<vector<int>> vs;
	vector<int> v;
	dfs(0, nums, v, vs);
	return vs;
}

bool checkDecreasing(vector<int>& arr, vector<int>& increasingArray)
{
	int lastRemain = INF;
	size_t j = 0;
	for (int i = 0; i < arr.size(); ++i)
		if (j < increasingArray.size() && arr[i] == increasingArray[j])
			++j;
		else if (arr[i] <= lastRemain) // check for decreasing ( = means not severe control)
				lastRemain = arr[i];
		else
			return false;
	return true;
}

bool checkSimple(vector<int>& arr)
{
	//vector<int> dp = getdp2(arr);
	//vector<int> increasingArray = generateLIS(arr, dp);
	vector<vector<int>> vs = findIncreasingArray(arr);
	if (vs.size() <= 0)
		return false;
	else
	{
		for (size_t vCnt = 0; vCnt < vs.size(); ++vCnt)
			if (checkDecreasing(arr, vs[vCnt]))
				return true;
		return false;
	}
}

int countSimple(vector<int>& arr)
{
	int cnt = 0;
	for (int i = 0; i < arr.size(); ++i)
	{
		vector<int> sub_arr;
		for (int j = i; j < arr.size(); ++j)
		{
			sub_arr.push_back(arr[j]);
			if (sub_arr.size() <= 2 || checkSimple(sub_arr))
				cnt += 1;
		}
	}
	return cnt;
}



int main()
{
	rewind(stdin);
	fflush(stdin);
	int totalCnt = 0;
	cin >> totalCnt;
	if (totalCnt <= 0)
		return EXIT_FAILURE;
	vector<string> line;
	for (int i = 0; i < totalCnt; ++i)
	{
		line.push_back("");
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
	}
	for (int i = 0; i < totalCnt; ++i)
	{
		vector<int> arr = parseLine(line[i], ' ');
		cout << countSimple(arr) << endl;
	}
}

编译、运行,0 error、0 warning,答案正确!好开心啊!
而且作为一名网络空间安全专业的本科生,想想加入了 rewind(stdin)、fflush(stdin) 等来防止有人胡乱输入,也过滤了输入负数的情况,还容纳输入测试数据时有人不小心输入了字母、特殊字符等的错误,还容纳了一行多个空格一行头尾有多余空格等错误。
现在看着,貌似第二、四、六行都是多余的吧!

结果,师兄和我说,题目要在某平台上提交,一运行就是 wrong answer。
那我懂了,面对一次性输入控制台的机器,用 rewind(stdin) 就寄了。这时候也逐渐明白了为什么还要给出一共多少个测试样例和每个测试样例一共多少个数据了。
既然是机器,那就不怕它乱输入了(就算它乱输入也是平台的问题与我无瓜)。
修改输入代码:

#include <iostream>
//#include <string>
#include <vector>
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EOF
#define EOF (-1)
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef INF
#define INF 0x7FFFFFFF
#endif
using namespace std;


/* handle input string */
/*
vector<int> parseLine(const string& _line, char sep)
{
	vector<int> v;
	string line = "";
	for (int i = 0; i < _line.size(); ++i)
		if ((_line[i] >= '0' && _line[i] <= '9') || (_line[i] == sep && (line.size() > 0 && line[line.size() - 1] != sep)))
			line += _line[i];
	if (line.size() <= 0)
		return v;
	while (line[line.size() - 1] == sep)
		line = line.substr(0, line.size() - 1);
	if (line.size() <= 0)
		return v;
	size_t pos = 0, pos2 = 0, len = line.length();
	while (pos < len)
	{
		pos2 = line.find(sep, pos);
		if (pos2 == -1)
			pos2 = len;
		v.push_back(atoi(line.substr(pos, pos2 - pos).c_str()));
		pos = pos2 + 1;
	}
	return v;
}
*/


/* handle problem LIS */
/*
vector<int> getdp2(vector<int>& arr)
{
	vector<int> dp(arr.size(), 0);
	vector<int> ends(arr.size(), 0);
	ends[0] = arr[0];
	dp[0] = 1;
	int right = 0, l = 0, r = 0, m = 0;
	for (int i = 1; i < arr.size(); ++i)
	{
		l = 0;
		r = right;
		while (l <= r)
		{
			m = (l + r) / 2;
			if (arr[i] > ends[m])
				l = m + 1;
			else
				r = m - 1;
		}
		right = max(right, l);
		ends[l] = arr[i];
		dp[i] = l + 1;
	}
	return dp;
}

vector<int> generateLIS(vector<int>& arr, vector<int>& dp)
{
	int len = 0, index = 0;
	for (int i = 0; i < dp.size(); ++i)
		if (dp[i] > len)
		{
			len = dp[i];
			index = i;
		}
	vector<int> lis(len, 0);
	lis[--len] = arr[index];
	for (int i = index; i >= 0; --i)
		if (arr[i] < arr[index] && dp[i] == dp[index] - 1) // find subarray from back to front
		{
			lis[--len] = arr[i];
			index = i;
		}
	return lis;
}
*/


/* Handle current problem */
void dfs(int x, vector<int>& nums, vector<int>& v, vector<vector<int>>& vs)
{
	if (x == nums.size())
	{
		if (v.size() >= 1) // exit loop
			vs.push_back(v);
		return;
	}
	if (v.size() == 0 || nums[x] >= v[v.size() - 1]) // select ( = means not severe control)
	{
		v.push_back(nums[x]);
		dfs(x + 1, nums, v, vs);
		v.pop_back(); // back
	}
	if (v.size() == 0 || nums[x] != v[v.size() - 1])
		dfs(x + 1, nums, v, vs); // do not select
	return;
}

vector<vector<int>> findIncreasingArray(vector<int>& nums)
{
	vector<vector<int>> vs;
	vector<int> v;
	dfs(0, nums, v, vs);
	return vs;
}

bool checkDecreasing(vector<int>& arr, vector<int>& increasingArray)
{
	int lastRemain = INF;
	size_t j = 0;
	for (int i = 0; i < arr.size(); ++i)
		if (j < increasingArray.size() && arr[i] == increasingArray[j])
			++j;
		else if (arr[i] <= lastRemain) // check for decreasing ( = means not severe control)
			lastRemain = arr[i];
		else
			return false;
	return true;
}

bool checkSimple(vector<int>& arr)
{
	//vector<int> dp = getdp2(arr);
	//vector<int> increasingArray = generateLIS(arr, dp);
	vector<vector<int>> vs = findIncreasingArray(arr);
	if (vs.size() <= 0)
		return false;
	else
	{
		for (size_t vCnt = 0; vCnt < vs.size(); ++vCnt)
			if (checkDecreasing(arr, vs[vCnt]))
				return true;
		return false;
	}
}

int countSimple(vector<int>& arr)
{
	int cnt = 0;
	for (int i = 0; i < arr.size(); ++i)
	{
		vector<int> sub_arr;
		for (int j = i; j < arr.size(); ++j)
		{
			sub_arr.push_back(arr[j]);
			if (sub_arr.size() <= 2 || checkSimple(sub_arr))
				cnt += 1;
		}
	}
	return cnt;
}



int main()
{
	/*
	rewind(stdin);
	fflush(stdin);
	int totalCnt = 0;
	cin >> totalCnt;
	if (totalCnt <= 0)
		return EXIT_FAILURE;
	vector<string> line;
	for (int i = 0; i < totalCnt; ++i)
	{
		line.push_back("");
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
		rewind(stdin);
		fflush(stdin);
		getline(cin, line[i]);
	}
	for (int i = 0; i < totalCnt; ++i)
	{
		vector<int> arr = parseLine(line[i], ' ');
		cout << countSimple(arr) << endl;
	}
	*/

	int totalCnt = 0;
	cin >> totalCnt;
	if (totalCnt <= 0)
		return EXIT_FAILURE;
	vector<string> line;
	for (int i = 0; i < totalCnt; ++i)
	{
		int perLineCnt = 0;
		cin >> perLineCnt;
		if (perLineCnt <= 0)
			return EXIT_FAILURE;
		vector<int> arr(perLineCnt);
		for (int j = 0; j < perLineCnt; ++j)
			cin >> arr[j];
		cout << countSimple(arr) << endl;
	}

	return EXIT_SUCCESS;
}

搞定,美滋滋!起初认为就这么结束了,没想到啊!师兄说平台有运行限时,实际上这才刚刚开始。想想有没有可以优化的。
回顾下自己的过程:思路是遍历一个序列的所有子序列,对子序列遍历所有递增子子序列,判断剩余的是否递减序列。
这么看,遍历一个序列的所有子序列好像就要两层 for 了(一开始我是三层),遍历递增又要一层,判断剩余递减至少又一层。既然这样,那就优化下 checkSimple 的逻辑吧!

第二天,我想到了可以一个元素一个元素遍历,然后看看它能否被塞到不严格递增序列或不严格递减序列里面。英雄所见略同(其实我不是英雄),我发现有其它博主也有这样的问题和解决方案,只不过他们比这个问题少了一层主序列的循环和计数:
https://blog.csdn.net/cat_hate_fish/article/details/113041688

按照自己风格整合了下代码:

#include <iostream>
#include <vector>
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EOF
#define EOF (-1)
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef INF
#define INF 0x7FFFFFFF
#endif
using namespace std;


bool checkSimple(vector<int>& a)
{
	size_t n = a.size();
	vector<int> asc, des;
	asc.push_back(EOF);
	des.push_back(INF);
	for (size_t i = 0; i < n; ++i)
	{
		int ab = asc.back(), db = des.back();
		if (ab < a[i] && db <= a[i]) // increasing subsequence only
			asc.push_back(a[i]);
		
		else if (ab >= a[i] && db > a[i]) // decreasing subsequence only
			des.push_back(a[i]);
		
		else if (ab >= a[i] && db <= a[i]) // no subsequences allowed
			return false;
		
		else//if (ab < a[i] && db > a[i]) // both subsequences allowed
		{
			if (i == n - 1)
				asc.push_back(a[i]);
			else
			{
				if (a[i] < a[i + 1])
					asc.push_back(a[i]);
				else if (a[i] > a[i + 1])
					des.push_back(a[i]);
				else
					asc.push_back(a[i]);
			}
		}
	}
	return true;
}

int countSimple(vector<int>& arr)
{
	int cnt = 0;
	for (int i = 0; i < arr.size(); ++i)
	{
		vector<int> sub_arr;
		for (int j = i; j < arr.size(); ++j)
		{
			sub_arr.push_back(arr[j]);
			if (sub_arr.size() <= 2 || checkSimple(sub_arr))
				cnt += 1;
		}
	}
	return cnt;
}



int main()
{
	int totalCnt = NULL;
	cin >> totalCnt;
	if (totalCnt <= NULL)
		return EXIT_FAILURE;
	vector<string> line;
	for (int i = 0; i < totalCnt; ++i)
	{
		int perLineCnt = NULL;
		cin >> perLineCnt;
		if (perLineCnt <= NULL)
			return EXIT_FAILURE;
		vector<int> arr(perLineCnt);
		for (int j = 0; j < perLineCnt; ++j)
			cin >> arr[j];
		cout << countSimple(arr) << endl;
	}

	return EXIT_SUCCESS;
}

同时我注意到,上文博主用的文件头是<bits/stdc++.h>。好吧,对于我这个整天用 Visual Studio 编写内核驱动的人来说,这种文件头在 VS 中是编译都过不了的了。但也很有可能告诉我,这个平台上处理的代码应该是这个文件头开头的,而不是 #include <iostream>等。

问题又来了,师兄说,还是超时。随后,师兄告诉我,要 1s 内完成。
我们来比较一下上面那位博主的题目和我们的题目,那位博主的题目是 2s 完成本题目中判断子序列中是否可以被分割成一个不严格递增子子序列和一个不严格递减子子序列,而本题是 1s 完成序列的所有子序列是否可以被分割成一个不严格递增子子序列和一个不严格递减子子序列。

第三天,我直接考虑对主序列找出不严格递增子序列的个数,然后再从剩余序列中查找不严格递减子序列的个数进行计数,妄想绕过子序列的遍历直接统计个数。这个方法我对第一个测试样例画了画就否决了,因为存在重复计数。也就是说,问题是重叠子问题,不是最优子结构。我也考虑过使用像 Python 一样的 set 进行过滤。后来想想,题目只允许用 C/C++,那还是算了吧!

随后我还是考虑拆分问题,开始画趋势,想找到新的方法。结果真找到了!
首先看

1, 2, 3, 4, 5, 4, 3, 2, 1

先上去再下来的,应该是没问题的。

3, 2, 1, 2, 3

先下来再上去也可。

1, 6, 3, 4, 5, 2

一个不严格递增序列和一个不严格递减序列穿插也可。

2, 1, 4, 3

无法实现。

3, 4, 1, 2

无法实现。
哦,那到这大概懂了,避开“2143”和“3412”就可以了。
问了下师兄,平台对这道题给的空间还是很宽裕的,还给了测试样例的上下限,那我就 vector 也不用了,直接上数组吧!(实战时绝不可能这么处理因为往往会有不少不会用的用户在你的程序上输入各种非数字字符且更倾向于每次只输入序列,上文的容错就很有必要了)(这个具体问题具体分析,总不能写个 Hello World 把内核驱动自我保护都用上以防止破坏和注入)
重新捣鼓代码:

#include <iostream>
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EOF
#define EOF (-1)
#endif
#ifndef NULL
#define NULL 0
#endif
using namespace std;
const int N = 2e5 + 10;
int totalCnt = NULL, n = NULL, a[N] = { NULL }, dp[N] = { NULL }, pd[N] = { NULL }, f[N] = { NULL };
long long int answer = NULL;


inline void update(int i)
{
	if (n < i)
		return;
	int inc = 0, dec = n + 1;
	if (pd[i - 1] < a[i])
		inc = max(inc, a[i - 1]);
	if (a[i - 1] < a[i])
		inc = max(inc, dp[i - 1]);
	if (a[i] < dp[i - 1])
		dec = min(dec, a[i - 1]);
	if (a[i] < a[i - 1])
		dec = min(dec, pd[i - 1]);
	if (inc == dp[i] && dec == pd[i])
		return;
	dp[i] = inc, pd[i] = dec;
	f[i] = 0;
	if (dec <= n || inc)
	{
		update(i + 1);
		f[i] = f[i + 1] + 1;
	}
	return;
}



int main()
{
	cin >> totalCnt;
	if (totalCnt <= NULL)
		return EXIT_FAILURE;

	for (int cnt = 0; cnt < totalCnt; ++cnt)
	{
		/* Initial */
		answer = NULL;
		memset(a, NULL, sizeof(a));
		memset(dp, NULL, sizeof(dp));
		memset(pd, NULL, sizeof(pd));
		memset(f, NULL, sizeof(f));

		/* Input per case */
		cin >> n;
		if (n <= NULL)
			return EXIT_FAILURE;

		/* Handle per case */
		for (int i = 1; i <= n; ++i)
			cin >> a[i];
		for (int i = n; 1 <= i; --i)
		{
			dp[i] = n + 1;
			pd[i] = 0;
			update(i + 1);
			f[i] = f[i + 1] + 1;
			answer += f[i];
		}
		cout << answer << endl;
	}

	return EXIT_SUCCESS;
}

听说正解一般用#include <bits/stdc++.h>,不喜欢太多的宏、过滤和注释,那我就调整下吧!
最后代码(代码量都少不少):

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int totalCnt, n, a[N], dp[N], pd[N], f[N];
long long answer;


inline void update(int i)
{
	if (n < i)
		return;
	int inc = 0, dec = n + 1;
	if (pd[i - 1] < a[i])
		inc = max(inc, a[i - 1]);
	if (a[i - 1] < a[i])
		inc = max(inc, dp[i - 1]);
	if (a[i] < dp[i - 1])
		dec = min(dec, a[i - 1]);
	if (a[i] < a[i - 1])
		dec = min(dec, pd[i - 1]);
	if (inc == dp[i] && dec == pd[i])
		return;
	dp[i] = inc, pd[i] = dec;
	f[i] = 0;
	if (dec <= n || inc)
	{
		update(i + 1);
		f[i] = f[i + 1] + 1;
	}
	return;
}



int main()
{
	cin >> totalCnt;
	for (int cnt = 0; cnt < totalCnt; ++cnt)
	{
		answer = 0;
		memset(a, 0, sizeof(a));
		memset(dp, 0, sizeof(dp));
		memset(pd, 0, sizeof(pd));
		memset(f, 0, sizeof(f));
		cin >> n;
		for (int i = 1; i <= n; ++ i)
			cin >> a[i];
		for (int i = n; 1 <= i; -- i)
		{
			dp[i] = n + 1;
			pd[i] = 0;
			update(i + 1);	
			f[i] = f[i + 1] + 1;
			answer += f[i];
		}
		cout << answer << endl;
	}
 
	return 0;
}

问题解决!六步绝杀!(最近下中国象棋下傻了,ACM 上好像叫 AC?)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值