一、爱丽丝的人偶
题目解析

现在存在
n
个玩偶,每个玩偶的身高是1、2、3......n
;现在我们要对这些玩偶进行排序(如果x人偶,它左右两边的玩偶一个比x高、一个比x矮,那这个玩偶就会爆炸)。
我们不想要任何一个人偶爆炸,题目输入一个
n
,让我们返回满足条件的排列。
算法思路
这道题,可以说比较简单了,我们要让i
人偶比i-1
和i+1
位置的人偶高或者低;
我们可以这样去排列:
1, n , 2 , n-1 , 3 , n-2......
这样我们可以发现,任何一个位置,它都是比左右两个人偶高或者低的;
那这道题我们就可以从1
和n
开始放置人偶 ,所以只需要定义两个变量分别从1
和n
开始向中间遍历。
**这里有一个要注意的点:**当我们放置完
l
人偶后,l
是可能等于r
;如果我们不做判断,那就可能放置两个身高一样的人偶:
1 3 2 2
。所以我们要进行判断,如果放置完
l
的人偶后,让l++
,再判断l
是否小于等于r
:(如果l<=r
就放置r
,否则就不放置)。
这里我们也没有必要将数据存储起来,直接输出即可。
代码实现
cpp
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int l = 1,r = n;
while(l<=r)
{
cout<<l<<' ';
l++;
if(l<=r)
{
cout<<r<<' ';
r--;
}
}
cout<<endl;
return 0;
}
二、集合
题目解析

题目给定两个集合(数组)
A
和B
,让我们求出A
和B
的交集(A
+B
);输出时要求按照升序输出。
算法思路
对于这道题,就类似于归并排序中,合并两个数组的操作,但是这道题目求的是交集,也就是最后结果中不能存在相同的元素。
思路一:排序+合并数组
对于
A
和B
,题目并没有说这两个数组是有序的,那我们要进行和并数组操作就要先对A
和B
进行排序;排完序之后,我们使用
l
和r
分别遍历两个数组(注意:要求升序且不能存在重读元素)如果
l
位置元素等于r
位置元素就先加入到结果数组中,让r++
(或者l++
);如果
l
位置元素大于r
位置元素,就将r
位置元素加入到结果数组中;如果
l
位置元素小于r
位置元素,就将l
位置元素加入到结果数组中。最后遍历结束之后,
l
或r
可能没有遍历完,所以要将A
或者B
中剩余的元素加入到结果数组中。
思路二:set
去重+排序
对于这种思路就有一点投机取巧了:
set
中是不允许出现重复元素的,并且set
底层是红黑树,是一个平衡搜索二叉树;使用迭代器遍历是有序的。
代码实现
cpp
#include <iostream>
#include <set>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
//set去重+排序
set<int> ret;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
ret.insert(x);
}
for (int i = 0; i < m; i++) {
cin >> x;
ret.insert(x);
}
for (auto& e : ret) {
cout << e << ' ';
}
cout << endl;
}
三、最长回文子序列
题目解析

题目给定一个字符串
str
,然后让我们在这个字符串中找到最长的回文子序列,然后输出它的长度。
算法思路
对于这道题,初看可能没什么思路
这里我们可以选取不连续的元素构成子序列,那这如何去找啊?
先说整道题用什么方法去解决:动态规划
要使用动态规划去解决这道问题,那我们就要搞清楚状态表示和状态转移方程
状态表示:
dp[i][j]
表示区间[i,j]
内最长的回文子序列的长度**状态转移方程:**对于状态转移方程,这里就要好好的分析一下了:
dp[i][j]
表示的是区间[i,j]
内最长的回文子序列的长度,但是可能i>j
或者i == j
或者i<j
i > j
:这种情况肯定是不存在区间[i,j]
的,那此时dp[i][j] = 0
。
i == j
:这种情况,区间[i,j]
只有一个元素,那此时这一个元素它可以构成一个回文子序列;所有dp[i][j] = 1
。
i < j
:这种情况,区间[i,j]
内是大于等于2
个元素的;这时候我们就要看i
位置和j
位置是否相等了。如果
str[i] == str[j]
,区间[i,j]
中最长回文子序列的长度就等于[i+1,j-1]
中最长回文子序列的长度再加上2
。如果
str[i] == str[j]
,我们不能直接去找区间[i+1,j-1]
内的最长回文子序列 ,因为i+1
位置的元素和j
位置的元素、i
位置的元素和j-1
位置的元素是可能相等的;所以我们要找的就是区间[i+1,j]
和区间[i,j-1]
中最长回文子序列长度的最大值。填表顺序: 这里我们填
dp[i][j]
时用到了dp[i][j-1]
、dp[i+1,j]
和dp[i+1,j-1]
;那我们填表的顺序:从下到上,每一行从左到右。**初始化:**我们在填第
n-1
行要用到第n
行的数据、填写第1
列时要用到第0
列的数据,所以我们要初始化第n
行和第0
列。**返回:**我们最后要的结果在
dp[0][n-1]
里存着(区间[0,n-1]
内最长回文子序列的长度),最后输出dp[0][n-1]
即可。

代码实现
cpp
#include <iostream>
using namespace std;
const int N = 1001;
int dp[N][N] = {0};
int main() {
string str;
cin>>str;
for(int i = str.size()-1;i>=0;i--)
{
for(int j = 0;j<str.size();j++)
{
if(i>j)
dp[i][j] = 0;
else if(i == j)
dp[i][j] = 1;
else
{
if(str[i] == str[j])
dp[i][j] = dp[i+1][j-1]+2;
else
dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
}
}
}
cout<<dp[0][str.size()-1]<<endl;
return 0;
}
到这里本篇文章内容就结束了
感谢各位的支持