Topic 1:经此一役小红所向无敌(数学归纳)
好中二的名字
拿到之后发现很简单
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long a, h, b, k;
cin >> a >> h >> b >> k; // a: 对立攻击, h: 对立血量, b: 光攻击, k: 光血量
long long res = 0;
while (true)
{
// 普通攻击阶段
res += a + b;
// 互殴阶段
h -= b;
k -= a;
// 检查死亡情况
if (h <= 0 && k <= 0)
{
// 同时死亡,无人放大招
break;
}
else if (h <= 0)
{
// 对立死,光放大招
res += b * 10;
break;
}
else if (k <= 0)
{
// 光死,对立放大招
res += a * 10;
break;
}
// 否则继续下一轮
}
cout << res << endl;
return 0;
}
但是并不是这样,这种模拟在10的9次方数据量下会超时的,不健壮,应该数学归纳
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long a, h, b, k;
cin >> a >> h >> b >> k;
// 计算还能活几轮(向上取整) 向上取整公式x / y: (x + y - 1) / y
long long t1 = (h + b - 1) / b; // 对立还能活 t1 轮
long long t2 = (k + a - 1) / a; // 光还能活 t2 轮
long long res = (a + b) * min(t1, t2);
if (t1 == t2)
{}// 同归于尽,没有大招,无事发生
else if (t1 < t2) // 对立先死,光放大招
{
res += b * 10;
}
else // 光先死,对立放大招
{
res += a * 10;
}
cout << res << endl;
return 0;
}
向上取整公式 x / y: (x + y - 1) / y -> 5 / 2 = 2.5 向下取整 =2 向上取整 =3 ,向上取整经常用来处理回合制游戏里面的刀数计算
Topic 2:连续子数组最大和(贪心、动态规划)
今天不偷懒,贪心和动归都写一遍当复习。
贪心法:贪心的核心思路在于——连续和 + num[i] 和num[i]之间的关系,什么意思呢?
就是当遍历到i位置时,i之前所有的数字加起来的和——也就是连续和,再加上i位置的数,如果比num[i]本身的数字还小,那么就证明之前的连续和是负数,负资产只会拖累总值,所以不如舍弃掉,从i的位置重新算起,然后遍历过程中用一个res记录遍历过程曾经达到的最大值,最后输出即可;
class Solution
{
public:
int FindGreatestSumOfSubArray(vector<int>& array)
{
int res = array[0], con = array[0];// 结果和连续和都初始化为第一个数
for(int i = 1; i < array.size();i++)// 从第2个数开始遍历
{
con = max(array[i], con + array[i]);// 如果连续和+i 还不如i本身,就意味着连续和是负数,
//把它舍弃掉,从i重新开始计算
res = max(res, con);// res会记录过程中所达到的顶峰值
}
return res;
}
};
这也叫做Kadane's Algorithm(卡拉兹算法),核心思想是丢弃对后续计算无益的部分。
某些题目中会要求将负数or 0,以0输出,可以稍稍修改成,当当前子数组的和小于0时,将con重置为0即可,灵活应变反正核心思想是不变的。
动态规划:动归解决此问题的核心:dp[i]——i位置子序列的最大和;递推公式:dp[i] = max(nums[i], dp[i - 1] + nums[i]), 其实和贪心的算法类似,也是取i位置的值,还是取之前位置的连续和的问题;接下来初始化,dp[0] 初始化为nums[0],dp[1]初始化为max(nums[1], nums[0] + nums[1]); 然后从头遍历即可
class Solution
{
public:
int FindGreatestSumOfSubArray(vector<int>& array)
{
int n = array.size();
vector<int> dp(n); // dp[i] 表示以 nums[i] 结尾的最大连续子数组和
dp[0] = array[0]; // 初始化
int res = dp[0]; // 初始最大值
for (int i = 1; i < n; ++i)
{
dp[i] = max(array[i], dp[i - 1] + array[i]);// 状态转移方程
res = max(res, dp[i]); // 更新最大值
}
return res;
}
};
其实卡拉兹算法可以看作就是动规的空间优化版,没用dp数组的空间而已
Topic 3:非对称之美(贪心)
原来做了一个对称之美,现在来一非对称
这题有个陷阱,如果按照我们常规判断回文的方式去解决,那一个长字符串里可以有n多个回文,我一个一个去找?不好判断,代码也不好写,效率也差,所以需要从“非回文串”的部分来入手,那么我们要找到一个最长的非回文串,首先根据最长锁定到以整个回文串为目标,然后回文串的特性注定了它是沿中轴对称的,那么如果这一整个字符串是回文串,那我把它最后一个字母拿走,破坏了它的中轴平衡,此时它就不是一个回文串了,那如果这一整个字符串不是回文串,那就更简单了,直接返回即可;那么此事还需要注意一个额外情况,就是全串是同一个字母的情况,特殊处理一下输出一个字母数即可;
#include <bits/stdc++.h>
using namespace std;
bool isPa(const string& s)
{
int l = 0, r = s.size() - 1;
while (l < r)
{
if (s[l] != s[r]) return false;
++l; --r;
}
return true;
}
int main()
{
string s;
cin >> s;
bool allSame = true;// 判断是否全是相同字符
for (const auto& c : s)
{
if (c != s[0])
{
allSame = false;
break;
}
}
if (allSame) cout << 0 << endl;// 1个字符也被视作回文,所以这里要返回0————找不出非回文
else if (!isPa(s)) cout << s.size() << endl;
else cout << s.size() - 1 << endl;
return 0;
}