C++日常刷题积累
- 今日刷题汇总 - day010
- 1、最长回文子串
- 1.1、题目
- 1.2、思路
- 1.3、程序实现
- 2、买卖股票的最好时机(一)
- 2.1、题目
- 2.2、思路
- 2.3、程序实现
- 2.4、程序实现 -- 优化
- 3、[NOIP2002 普及组] 过河卒
- 3.1、题目
- 3.2、思路
- 3.3、程序实现 -- dp
- 4、题目链接
今日刷题汇总 - day010
1、最长回文子串
1.1、题目
1.2、思路
读完了题知道,在一个长度为n的字符串中,求最长回文子串的长度。回文子串可以理解为对称的字符串。因为具有对称性,那么基本思路就是“中心扩展法”,也就是依次字符串,然后遍历到该字符就向其两边扩展,如果两边的字符相等,那么就记录到retlen变量中,遍历完最后得到最大长度返回即可。为了方便理解,画个图:
此外,分析示例,还得注意奇数偶数的区别,那么接下来就是程序实现。
1.3、程序实现
首先,按照思路分析的“中心扩展法”,遍历字符串,且从i处从中心站展开,依次求得的retlen,与retlen不断比较更新,然后又因为需要区分奇数和偶数的情况,所以分别求得最大值,最后再比较依次得到最终最大的retlen返回即可。
class Solution
{
public:
int getLongestPalindrome(string A)
{
size_t len = A.size();
int left = 0;
int right = 0;
int retlen = 0;
//偶数
for(int i = 0;i < len; i++)
{
left = i;
right = i + 1;
while(left >= 0 && right < len && A[left] == A[right])
{
left--;
right++;
}
retlen = max(retlen ,right - left - 1);
}
//奇数
for(int j = 0;j < len;j++)
{
left = j;
right = j;
while(left >= 0 && right < len && A[left] == A[right])
{
left--;
right++;
}
retlen = max(retlen ,right - left - 1);
}
return retlen ;
}
};
2、买卖股票的最好时机(一)
2.1、题目
2.2、思路
读完题,知道对于一组股票的买卖机制,只能买卖一次,让求得获得的利润的最高收益,如果无论什么时候买入卖出都是亏,不管亏多少,即没有利润则输出0即可。那么,基本思路就是枚举/蛮力法,求得每一组的利润差,返回最大值,尝试过后,发现两层for会超时,此题限制1ms解决。因此,需要在蛮力法基础上优化,所以蛮力法也写一写,然后,基于蛮力法,回溯重复了太多次,进行优化,思考发现,如果我们反过来逆向思维,先考虑卖出的价值,然后,就只需要求得该卖出点之前的最小价值即可,得到的差就是最大差,也就是说只需要遍历一遍即可。接下来就是程序实现。
2.3、程序实现
首先,根据蛮力法思路分析,依次枚举所有情况,求得最大差值输出即可,此题会超时。所以得进行优化。
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int arr[N];
int main()
{
int n;
cin >> n;
for(int i = 0;i < n;i++)
cin >> arr[i];
int maxval = 0;
for(int i = 0;i < n; i++)
{
for(int j = i;j < n;j++)
{
maxval = max(maxval , arr[j]- arr[i]);
}
}
cout << maxval << endl;
return 0;
}
2.4、程序实现 – 优化
基于上述的蛮力法,进行优化,为了方遍理解,根据思路分析画个演示图:
那么程序实现,按照要求写好输入,然后定义minval初始化arr[0]当作假设的最小值进行遍历比较更新即可,再定义一个maxSub表示遍历至 i 位置时,与minval的最大差值,值得注意的是,遍历时注意minval和maxSub的顺序性,先求最小值minval再求maxSub即可。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n = 0;
cin >> n;
vector<int> arr(n);
int m = 0;
while(n--)
{
cin >> arr[m];
m++;
}
int minval = arr[0];
int submax = 0;
for(int i = 0;i<arr.size();i++)
{
minval = min(arr[i],minval);
submax = max(submax, arr[i] - minval);
}
cout << submax << endl;
return 0;
}
3、[NOIP2002 普及组] 过河卒
3.1、题目
3.2、思路
读完题,知道让求从A点按照一定的规则,走到B点最多有多少条的路径。分析题目需要知道,按照一定的规则,可以向右或向下走就想到了动态规划dp思路,然后题目中还需要注意的是,马的控制点不能被走(访问),也就是象棋中马在坐标上走的斜"日"所设计的点,且包括马的起始点都被称为控制点。其中,马是一开始就给出的固定点(x,y),且题目也给了马跳跃点与马起始点的关系。此外,还得注意的是,根据示例和棋盘得知,棋盘的大小是(n+1)(m+1)。
所以,综合所述,思考分析得出:
(1)、可使用动态规划dp思路解题;
(2)、马的控制点,除了斜“日”外,还包括自身的起始位置;
(3)、棋盘的大小是(n+1)(m+1).
所以分析了注意点后,那么就回归到动态规划的dp状态表示和状态转移方程上来;
由题目的走的规则,定义dp[i][j]状态表示:走到该位置最多有几条路径;
推导状态转移方程:dp[i][j] = d[i][j-1] + d[i-1][j] ;
此外注意如果B点起始位置就与马的起始位置或控制点重合,还有就是B点的上方和左方全部被阻塞,即是控制点,那么以上极端情况,此时dp[i][j] = 0; 那么接下来,就是程序实现。
3.3、程序实现 – dp
首先,根据思路的分析写好输入,定义开辟好dp数组(两个坑点稍后说),根据棋盘的大小为了坐标能够统一描述所以这里就额外多开辟一行一列,那么x和y就需要映射坐标+=1即可,然后探究dp的初始化问题,画个图更清晰:
接着,实际二维数组就从[1,n+1]和[1,m+1]遍历,不断判断极端情况的处理即可,最后输出dp[n+1][m+1]即可。到此,思路没有问题,总结步骤为一下几点:
(1)、映射x和y的坐标;
(2)、根据(多开辟一层)数组定义初始化dp[0][1] =1 或 dp[1][0] = 1均可;
(3)、遍历二位数组,注意边界控制从[1,n+1]和[1,m+1]遍历;
a、判断极端情况:1.马的控制点阻塞路径 2.重合问题;
b、正常执行状态转移方程:dp[i][j] = d[i][j-1] + d[i-1][j] ;
(4)、最后输出dp[n+1][m+1]即可.
另外,上面提到的两个坑点,在于我写好后,提交不通过,发现,数据超范围了,所以最好使用long long开辟数组,还有一个坑点是在于开辟的大小范围由于多开辟的一层使用,所以这里至少大于等于22才行。之前使用dp[21][21]无法通过所有用例哈。
#include <iostream>
using namespace std;
long long dp[22][22];
int main()
{
int n,m,x,y;
cin >> n >> m >> x >> y;
//映射坐标
x += 1;
y += 1;
//初始化
dp[0][1] = 1;
//遍历
for(int i = 1;i <= n+1; i++)
{
for(int j = 1;j <= m+1; j++)
{
//极端情况的处理: 1.马控制点 2.自身重合
if((i != x && j != y && abs(i - x) + abs(j - y) == 3) || (i == x && j == y))
{
dp[i][j] = 0;
}
else
{
dp[i][j] = dp[i][j-1] + dp[i-1][j];
}
}
}
cout << dp[n+1][m+1] << endl;
return 0;
}
4、题目链接
最长回文子串
买卖股票的最好时机(一)
[NOIP2002 普及组] 过河卒