题目一
思路
区间DP
玩DP,先确定为什么是DP,再就是老四样,先找好几个状态,赋初始值,找转移,最后找答案。
确定算法
abba是回文串,那么bb一定是回文串,所以抽象出大问题abba是不是回文串,小问题bb是不是回文串,当bb是回文串即小问题成立,并且同时左右端点一致,,则推导出大问题成立,所以可以尝试DP算法
定状态
因为是子串,所以是连续的一个子序列,所以只需要表示出这个子序列的区间就行,所以用i,j表示区间即可。
找初始值(空串或者左一个右一个的字符)
回文串必须是左右两个节点一起判断,所以初始值是单个一个字符情况和两个字符情况。
找转移
正如上面分析所示(小问题怎么推出大问题),当两端一样,大区间的结果就看小区间的,所以
if (s[i] == s[j]) {
dp[i][j] = dp[i + 1][j - 1];
}
找答案
1、这道题其实关键就在找答案上,他要的最长子串,如果这题仅仅是求最长子串长度,那么用dp[][]即可,不是回文子串的就是0,反之存长度。
2、但这题要把子串找出来,所以用substr方法,需要知道起点+长度,所以两个信息存不下,那么dp[][] 正常存bool值,起点+长度单独计。因为我们寻找顺序就是区间由小到大,并且体重要求只需要长度最大,对长度相同的不同串都允许,所以我们只要找到一个回文子串,就记录起点+长度,因为我们从小到大的找,最后一次更新后,一定是最大的,即为答案。
代码
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
int begin = 0, max_length = 0;
bool dp[1050][1050] = { false };//初始化
for (int i = 0; i < n; i++) {//初始化
dp[i][i] = true;
begin = i;//判断是回文子串就更新起点加长度
max_length = 1;
}
for (int i = 0; i < n - 1; i++) {//初始化
if (s[i] == s[i + 1]) {
dp[i][i + 1] = true;
begin = i;//判断是回文子串就更新起点加长度
max_length = 2;
}
}
for (int l = 2; l < n; l++) {
for (int i = 0, j; i + l < n; i++) {
j = i + l;
if (s[i] == s[j]) {//当两端一样,大区间的结果就看小区间的
if (dp[i + 1][j - 1]) {//判断是回文子串就更新起点加长度
begin = i;
max_length = l + 1;
}
dp[i][j] = dp[i + 1][j - 1];
}
}
}
return s.substr(begin, max_length);
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N^2)
空间复杂度:O(N^2)
题目二:
思路
直接模拟题目要求,注意用STL的排序之前,要对结构体内部 operator < 进行重写
代码
//202009-1 称检测点查询
#include <iostream>
#include <algorithm>
#include <unordered_set>
using namespace std;
int n, x, y;//总点数,目标人物坐标
struct position {
int x;
int y;
int id;//监测点序号
long long distance;//距离,为防止溢出,所以开long long
bool operator < (const position& p)const {//覆写 operator <方法,为了使用sort函数
if (distance == p.distance) {//距离相等要编号小的
return id < p.id;
}
return distance < p.distance;//先比距离,要小的
}
};
position p[201];
int main()
{
cin >> n >> x >> y;
for (int i = 0; i < n; i++) {
scanf("%d %d", &p[i].x, &p[i].y);
p[i].id = i + 1;
p[i].distance = (p[i].x - x)*(p[i].x - x) + (p[i].y - y)*(p[i].y - y);
}
sort(p, p + n);//从小到大排序
for (int i = 0; i < 3; i++) {
printf("%d\n", p[i].id);
}
return 0;
}
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N)
题目三
思路:
直接模拟就行,但是注意,这里经过高风险的人数指的是一旦路过就算,而逗留指的是连续k个点都在高风险地区,二者分开统计
代码:
//202009-2 风险人群筛查
#include <iostream>
#include <algorithm>
#include <unordered_set>
using namespace std;
int n, k, t, xl, yd, xr, yu;//含义见题目
int main()
{
int ans_linger = 0;//逗留人数-只要经过高危区域就算
int ans_pass = 0;//途径人数
cin >> n >> k >> t >> xl >> yd >> xr >> yu;
for (int i = 0; i < n; i++) {//控制第i个人
int tmp_x, tmp_y, cnt = 0;
bool is_visited = false;//看是否经过高风险
bool is_judged = false;//是否连续k个点在高风险
for (int j = 0; j < t; j++) {//第i个人的第j个点(xj,yj)
scanf("%d %d", &tmp_x, &tmp_y);
if (tmp_x >= xl && tmp_x <= xr && tmp_y >= yd && tmp_y <= yu) {
cnt++;
is_visited = true;//路过即为true
if (cnt >= k && !is_judged) {//一旦连续k个点了
ans_linger++;
is_judged = true;//连续k点一次即可判定为逗留,之后不在判断
}
}
else {
if (cnt) {//这里是因为不是连续k个点,不算逗留,所以要清零
cnt = 0;
}
}
}
if (is_visited) {
ans_pass++;
}
}
printf("%d\n", ans_pass);
printf("%d", ans_linger);
}
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(N^2)