Coding Practice,48天强训(20)

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值