2023年第六届传智杯程序设计挑战赛初赛题解(部分)

本文分享了ACM/NOI/CSP/CCPC/ICPC等竞赛中涉及的多种编程题目,包括字符串操作、数组差值、删除公共字符、博弈论问题、前缀和计算以及素因数分解,旨在帮助参赛者提升技能和理解复杂算法
摘要由CSDN通过智能技术生成

  本次比赛的题目难度由低到高,涉及到了基本语法,博弈,贪心,动态规划,dfs等多个知识点,下面是我写的一些题解和一些官方题解,希望友子们看完能够学到些东西哈哈哈。

比赛连接:(16条未读私信) 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)icon-default.png?t=N7T8https://ac.nowcoder.com/acm/contest/71300#question

字符串拼接

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

int main() {
        
    string s1, s2;
    getline(cin, s1);
    getline(cin, s2);
 
    cout<<s1+s2<<endl;

    return 0;
}

差值

  将输入的值排序后枚举他们的差值找出最小值即可。

#include <iostream>
#include <algorithm>
using namespace std;
int a[1000000];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+1+n);
    int Min=a[n];
    for(int i=2;i<=n;i++)
    {
        if(a[i]-a[i-1]<Min)
            Min=a[i]-a[i-1];
    }
    cout<<Min<<endl;
}

删除公共字符

1.一个是注意用getline读取一整行字符串

2.用两个for循环删除第一个字符串中存在的第二个字符串中的字符,可以用string自带的erase函数删除字符,s.erase(int idex,int length),这里的idex是删除的字符在s中的下标,length是删除字符的长度,这里要逐个删除所以长度为1.

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s1,s2;
    getline(cin,s1);
    cin>>s2;
    for(int i=0;i<s2.size();i++)
    {
        for(int j=0;j<s1.size();j++)
        {
            if(s1[j]==s2[i])
                s1.erase(j,1);
        }
    }
    cout<<s1<<endl;
}

 红色和紫色

代码很简单但思路很重要,这是一个很经典的博弈论问题。首先我们要知道胜利的条件:

1.对方没有符合条件的格子可以染色.

2.相邻格子不能有相同颜色

所以我们可以先试着模拟下网格的模式,

我们发现当n*m为奇数时,网格存在中心,此时小红先手将中心染色的话,接下来只要在小紫染过色的格子中心对称的格子染色就一定会赢,可以模拟网格数位1,9...情况来验证,

当网格数为偶数时相反,无论小红先染哪个方格,小紫只要在和它中心对称的方格进行染色就一定会赢.可以模拟网格数为2,4,6,8....来验证

#include <iostream>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    if(n*m%2==0)
    {
        cout<<"yukari"<<endl;
    }
    else
        cout<<"akai"<<endl;
}

 abb(前缀和)

本题是需要找到一个字符串中存在多少个满足abb式的字符串.add条件为:

1.字符串长度为3

2.字符串后两位相同

3.字符串前两位不同

根据题目我们知道这个abb字符串并不是连续的,但又要满足以上条件,所以我们想到计算个数的方法是统计当前字符后面又多少个与它不同的字符个数,每个不同的字符又可以和他组成多少个abb式的字符串,首先想到的是暴力枚举字符串长度但是字符串的长度最大为10的5次方,所以肯定会超时,所以我们优化一下以用字母的个数来替代第二层循环,用二维数组sum[i][j]来记录i到n的字母个数情况,其中i表示从i到n,j表示字母的int值.得到这个数组之后在遍历一次计算出每个下标后面有多少个abb字符串即可,而每个字母能组成的个数为n*(n-1)/2,最后得出ans.

#include <iostream>
#include <string>
using namespace std;
int sum[100050][26];
int main()
{
    int n;
    cin>>n;
    string s;
    cin>>s;
    for(int i=n-1;i>=0;i--)
    {
        for(int j=0;j<26;j++)
        {
            sum[i][j]=sum[i+1][j];
        }
        sum[i][s[i]-'a']++;
    }
    long long res=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<26;j++)
        {
            if(s[i]-'a'!=j)
            res+=sum[i+1][j]*(sum[i+1][j]-1)*1.0/2;
        }
    }
    cout<<res;
}

kotori和素因子

题目的要求是找到输入的n个正整数的素因子,不同的正整数取不同的素因子,然后求他们和的最小值,我们先知道什么是素因子,素因子就是n%k==0并且k为质数,那么k就是n的素因子,然后我们要求这个数的素因子之和的最小值,因为数据范围比较小,所以我们可以找到每个正整数的所有素因子,然后通过暴力搜索遍历所有情况找到和最小的情况.

题目中还说如果没有最小的和就输出-1,这是什么意思呢?这表示所有正整数的素因子加起来不重复的个数小于正整数的个数,不满足每个正整数选择的素因子不同这一条件

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int n;//正整数的个数
bool flag[200]={0};  //每个质数是否被使用过
int num[10]; //n个正整数
int number[200]; //1到1000的质数
int cnt[10];    //每个正整数有多少个质因子
int idex; //1到1000的质数个数
int prime[10][200]; //第i个正整数的质因子从小到大排序 

int ans=10e8;
void f()
{
	for(int i=2;i<1000;i++)
	{
		int f=1;
		for(int j=2;j<=i/2;j++)
		{
			if(i%j==0) {f=0;break;}
		}
		if(f) number[idex++]=i;
	}
}
void dfs(int num,int sum)
{
	if(num==n)
	{
		if(sum<ans) ans=sum;
		return;
	}
	else
	{
		for(int i=0;i<cnt[num];i++)
		{
			if(!flag[prime[num][i]])
			{
				flag[prime[num][i]]=true;
				dfs(num+1,sum+number[prime[num][i]]);
				flag[prime[num][i]]=false;
			}
		}
	}
}
int main()
{
	f();
	cin>>n;
	for(int i=0;i<n;i++)
	cin>>num[i];
	//计算每个正整数的质因子并且保存他们在number中的下标 
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<idex;j++)
		{
			if(num[i]%number[j]==0) 
			{
				prime[i][cnt[i]++]=j;
			}
			if(number[j]>num[i])
			break;
		}
	} 
	int tt=0;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<cnt[i];j++)
		{
			if(!flag[prime[i][j]])
			{
				flag[prime[i][j]]=1;
				tt++;
			}
		}
	 } 
    if(tt<n) cout<<"-1";
    else
    {
    	memset(flag,0,sizeof(flag));
    	dfs(0,0);
    	cout<<ans<<endl;
    }
}

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值