每日小题7-25-2022

算法竞赛与编程技巧实战
博主分享了在河南萌新联赛和ACwing平台参与算法竞赛的经历,总结了多道编程题目的解题思路和赛后反思。对于暴力循环优化、质因数分解、数组处理和数学计算等编程技巧进行了探讨,强调了避免陷入思维定势和理解题目需求的重要性。

1

河南萌新联赛第二场:J:签到
链接:https://ac.nowcoder.com/acm/contest/37344/J

回顾:最初做的时候,想到了暴力循环的做法但是发现肯定超时,但是后来我想到了要把一个数移过去,这样可以降低复杂度,但是写的时候,我还是把它们写入了四个循环,这样造成的后果是:虽然思路是对了,但是我的代码量还是没变,属于是变了样子的暴力,看了题解后我才发现,我在查询的时候进行了无用的重复,于是我开了一个set存储d-c的值,再进行一个循环,总体是n方+n方*log2n的复杂度,

赛后我写的ac代码

#include<iostream>
#include<set>
using namespace std;
int arr[10010];
set<int> s;
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> arr[i];

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            s.insert(arr[i] - arr[j]);//先把右边的算出来

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            int k = arr[i] + arr[j];
            if (s.find(k) != s.end())
            {
                cout << "Yes";
                return 0;//一一旦找到一个直接退出,防止数据大超时
            }
    cout << "No";
    return 0;
}


2
河南萌新联赛第二场:F:手办
链接:https://ac.nowcoder.com/acm/contest/37344/F

回顾:当时写的时候在这道题上思考了很多,但是我陷入了一个误区,在我们的oj上有道与之类似但是却又更简单的题目,我陷入了那道题的思维,想的一直是从每个i入手,先判断平方的三次方是否是有理数,在判断/2,但是这里复杂度特别高,过肯定是不可能的。正确做法是:我们可以把k质因数分解,我们发现只要每个质因数的指数*2/3是个整数即可,也就是三的倍数,但是这里有一个漏洞,人家要的是x平方开三次方是有理数,那么x的开三次方在平方也是可以的,也就是x只要保证开三次方是有理数即可,我们就得到了一个极简代码

赛后的ac代码:

#include<iostream>
using namespace std;
typedef long long int LL;
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
        int k;
        cin >> k;
        int cnt = 0;
        for (LL i = 1; i*i*i <= k; i++)
        {
            if (k % (i*i*i) == 0)
                cnt++;
        }
        cout << cnt << endl;
    }
}


3
河南萌新联赛第二场:B:宝石


考试时由于看了几道题之后发现我的实力只会做一道题,所以后面的题目最后我都没有勇气去看,回过头才发现了这道有趣的题目,这道题和之前的a+b+c有点类似,我们发现他需要三个数相乘我上次做了一道题是两个数相加,并且很奇妙的是,我上次也做了一道题,和这代很类似的是,他们都是用后面的数,所以我很快想到了做法,很不幸的是,我的做法使用了set,但是会超时,他居然卡log2n的算法,非得开到O(1),于是在琢磨了好长时间的答案后,我终于写出了这道题,用时两个半小时;这里感谢超凡giegie给我讲解了比较难的一部分

赛后ac代码:

#include<iostream>
#include<set>
#include<unordered_map>
using namespace std;
unordered_map<int, int> mp;
int arr[100010];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);

    int ans = 0;
    for (int i = n; i >= 1; i--)
    {
        for (int j = i + 1; j <= n; j++) mp[arr[i + 1] * arr[j]]++;

        int flag = 0;
        for (int j = i + 1; j <= n; j++)
        {
            if (arr[i] && !arr[j]) continue;//如果下面是0并且上面不是零是错误的情况,直接排除,这里不能直接判断下面为零就错误,因为上面为0,下面也为0也是一种正确情况
            if (!arr[i] && !arr[j]) flag = 1;//上面为0,下面三个0相乘也正确;
            else if (arr[i] % arr[j] == 0 && mp[arr[i] / arr[j]]) flag = 1;//正常情况
        }
        if (flag == 1)
            ans++;
    }
    cout << ans << endl;
}

4:
acwing题目:https://www.acwing.com/problem/content/1383/

很简单的一道题,但是要注意的是,不能只取最后一位即可;

#include<iostream>
using namespace std;
typedef long long LL;
int main()
{
    int n;
    cin >> n;
    
    LL ans = 1;
    for (int i = 1; i <= n; i++)
    {
        ans *= i;
        if (ans > 1000000000)
            ans %= 1000000000;
        while (ans % 10 == 0)
        {
            ans /= 10;
        }
    }

    cout << ans % 10;
}


5:
题目:https://www.acwing.com/problem/content/1355/

这是来自acwing上面的一道题目,具有迷惑性;

最开始做的时候,我想的是让最高峰与最低峰往中间靠拢,使用了贪心思想,但是做到最后却发现,虽然这样做符合贪心思想,但是做的过程中却可能会因为最高峰与最低峰之间峰的分配问题而造成结果的改变,所以这道题并不适合贪心,查看过题解之后选择了了暴力做法,捎带一点判断即可;

这道题警示了我们,贪心思想首要是不改变后续的状态,这道题每一次选择过后会影响后续的判断,所以使用贪心会造成错误;

ac代码:

#include<iostream>
using namespace std;
const int N = 110;
int arr[N], an[N];

int main()
{
    int n;
    cin >> n;
    int min_int = 110, max_int = 0;
    for (int i = 1; i <= n; i++)
    {
        int k;
        cin >> k;
        arr[k]++;
        if (k > max_int)
            max_int = k;
        if (k < min_int)
            min_int = k;
    }
    if (max_int - min_int <= 17)
    {
        cout << "0";
        return 0;
    }

    int cnt = 0x3f3f3f3f;
    for (int i = 0; i <= 83; i++)
    {
        for (int j = 17; j <= 100; j++)
        {
            if (j - i == 17 && i >= min_int && j <= max_int)
            {
                int ans = 0;
                for (int k = min_int;k<= max_int; k++)
                {
                    if (k < i)
                        ans += arr[k] * (i - k) * (i - k);
                    else if (k > j)
                        ans += arr[k] * (k - j) * (k - j);
                }
                if (ans < cnt)
                    cnt = ans;
            }
        }
        
    }
    cout << cnt << endl;
}

6:
题目:https://www.acwing.com/problem/content/1605/

很简单的一道题,不需要多说;

代码:

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 200010;
int arr[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> arr[i];

    sort(arr + 1, arr + n + 1);
    for (int i = 1; i <= n / 2; i++)
    {
        arr[i] += arr[i - 1];
    }
    for (int i = n / 2 + 2; i <= n; i++)
    {
        arr[i] += arr[i - 1];
    }
    if (n % 2 == 0)
        cout << "0" << ' ' << arr[n] - arr[n / 2];
    else
        cout << "1" << ' ' << arr[n] - arr[n / 2];
}


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值