【算法竞赛学习】CSDN周赛第7期

奇偶排序

问题

给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数

题解

毕竟简单的方法就是先把奇数输出完,最后把偶数输出完,而不用考虑在数组里进行重组。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    int a[1001];
    cin >> n;
    for (int i = 0; i <= n - 1; i++)
    {
        cin >> a[i];
        if (a[i] % 2 != 0)
            cout << a[i] << " "; //先输出奇数
    }
    for (int i = 0; i <= n - 1; i++)
    {
        if (a[i] % 2 == 0)
            cout << a[i] << " "; //再输出偶数
    }
    return 0;
}

小艺照镜子

问题

已知字符串str。 输出字符串str中最长回文串的长度。

相关最长回文子串题目

题解

通过二维数组进行动态规划,dp[i][j]表示从第i个位置到第j个位置的字符串是否回文,若dp[i][j]=1则表示回文,dp[i][j]=0则表示不回文。

dp[i][j]是否为1,则是通过dp[i+1][j-1]是否为1来确定的。
当i=j时,表示一个位置的字符,同一个字符当然是相等的,则dp[i][j]=1。
而当i和j相邻时,则判断str[i]是否等于str[j]即可判断dp[i][j]是否为1。

所以转移方程是:
d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] dp[i][j]=dp[i+1][j-1] dp[i][j]=dp[i+1][j1]

核心代码:

class Solution {
public:
    string longestPalindrome(string s) {
        int n=s.size();
        vector<vector<int>>dp(n,vector<int>(n,0));
        int maxx=0;
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j])
                {
                    if(j-i==1||i==j)
                    {
                        dp[i][j]=1;
                    }
                    else
                        dp[i][j]=dp[i+1][j-1];
                }
                if(dp[i][j]==1&&j-i+1>maxx)
                {
                    maxx=j-i+1;
                }
            }
        }
        return maxx;
    }
};

相关题目

题解

class Solution {
public:

    string longestPalindrome(string s) {
        int n=s.size();
        string a(s);
        vector<vector<int>>dp(n,vector<int>(n,0));
        int maxx=0;
        int begin=0;
        for(int j=0;j<n;j++)
        {
            for(int i=0;i<=j;i++)
            {
                if(s[i]==s[j])
                {
                    if(j-i==1||i==j)
                    {
                        dp[i][j]=1;
                    }
                    else
                        dp[i][j]=dp[i+1][j-1];
                }
                
                if(dp[i][j]==1&&j-i+1>maxx)
                {
                    maxx=j-i+1;
                    begin=i;
                    
                }
            }
        }

        // return maxx;

        return a.substr(begin,maxx);
    }
};

交换后的or

问题

给定两组长度为n的二进制串,请问有多少种方法在第一个串中交换两个不同位置上的数字,使得这两个二进制串“或”的结果发生改变?

题解

由于交换导致的或关系改变只会涉及交换的4个数(即str1的左边,右边,str2的左边、右边),但str1的左边和str2的左边或关系是一个整体,所以也可以说只会涉及到两个整体。

我们使用二进制就可以表示位于同一个位置的不同串了,如第i个位置的10表示str1[i]=1,str2[i]=0,而一共有4种可能,故我们使用4个单位的数组存放。

具体细节和讲解在注释里讲了。

#include <bits/stdc++.h>
#include <string>
using namespace std;
typedef long long LL;
LL solution(int n, std::string str1, std::string str2){
    std::vector<int> ctr(4);
    for(int i = 0; i < n; ++i) {                           //     2  2
        int msk = (str1[i] == '1') << 1 | (str2[i] == '1');//00  01 10 11  = 0 1 2 3 
        ++ctr[msk];
    }
    LL result = 0;
    for(int i = 0; i < 4; ++i)
        for(int j = i + 1; j < 4; ++j) {
            if((i >> 1) == (j >> 1))//若str1的前一个数(i)和后一个数(j)一样,则无论怎么换位置都不会改变值
                continue;
            //>>1表示提取str1的数,&1表示提取str2的数,其余同理(i表示前一个数,j表示后一个数)
            int d0 = ((i >> 1) | (i & 1)) ^ ((j >> 1) | (i & 1));//表示str1的后一个数(j)能否改变str2的前一个(i)数的或关系,若能改变即右边的数和下面左边的数或关系与改变位置之前的或关系不一样(d0=1),则表示可以改变
            int d1 = ((j >> 1) | (j & 1)) ^ ((i >> 1) | (j & 1));//表示str1的前一个数(i)能否改变str2的后一个(j)数的或关系
            if(d0 || d1)
                result += (LL)ctr[i] * ctr[j];//ctr[]存放着有多少个类型不同的数,若有交换,则一共有str[i]*str[j]种
        }
    return result;
}
int main() {
    int n;
    std::string str1;
    std::string str2;
    std::cin>>n;
    std::cin>>str1;
    std::cin>>str2;
    LL result = solution(n, str1, str2);
    std::cout<<result<<std::endl;
    return 0;
}

去除整数(不讲)

题目

已知存在集合A包含n个整数,从1到n。 存在m个整数a[1…m]。 在集合A中去除这m个整数的的倍数。 输出集合中包含的元素的个数。

题解

容斥原理经典题
注意下最小公倍数超过有符号 32 位整型数的情况

这题有点忘记了,虽然感觉挺奇怪的,因为如果m中包含1,那么集合A就完全被去除了,这一点可能是因为我忘记题目样例是怎样的了,因为没有竞赛回顾的oj平台,有点难受。。。

附上大佬的代码

int solution(int n, int m, std::vector<int>& vec){
    typedef long long LL;
    int result = 0;
    for(int i = 0; i < (1 << m); ++i) {//1<<m 表示2^m
        int sgn = 1;
        LL lcm = 1;
        for(int j = 0; lcm <= n && j < m; ++j) {
            if((~i >> j) & 1)
                continue;
            sgn = -sgn;
            lcm = lcm / std::__gcd(lcm, (LL)vec[j]) * vec[j];
        }
        if(lcm <= n)
            result += sgn * (n / lcm);
    }
    return result;
}

一些建议

由于是第一次在CSDN打竞赛,所以对于平台很多东西都不是很懂,但有一点想吐槽一下,感觉回顾错题的时候挺不方便的,希望CSDN能够开一个OJ平台能够重新做一下之前的题,并且建议评测报告可以把数据样例附上不然都不知道自己做的对不对,而且论坛里也有不少写着周赛题解质量参差不齐,甚至答案都是错的,误导了许多人。。。虽然本人的题解也不一定是对的,但至少会有些思路(大概).

对了,其实我一直不知道平台oj的检测复制粘贴有什么意义,毕竟已经有跳出网页检测了,有时候不小心打错了很多字,,然后又不想再敲一遍就自然地复制粘贴(自己的代码),结果就被检测了,而且平台oj敲得也不舒服(如果有代码补全就更好了!)

希望平台能够看到~

参考

http://lihuaxi.xjx100.cn/news/100248.html

参考文献的思路挺好的,就是参考的网站乱码看的难受

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LenckCuak

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值