HFOI2017.9.12 复习赛01题解

3 篇文章 0 订阅
2 篇文章 0 订阅

01:质数的和与积

描述

两个质数的和是S,它们的积最大是多少?

输入
一个不大于10000的正整数S,为两个质数的和。
输出
一个整数,为两个质数的最大乘积。数据保证有解。
样例输入
50
样例输出
589
来源

《奥数典型题举一反三(小学五年级)》 (ISBN 978-7-5445-2882-5) 第三章 第二讲 例1


这是一道小学数学题啊。。。

我的方法是先用线性筛素数打出一个素数表,然后就i从S/2开始,往下减少,如果i和(S-i)都是质数,那么输出他们的积,并且return 0;

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
bool tf[10010]={true,true};//素数表
int s;
int main()
{
	for(int i=2;i<=100;i++)//线性筛素数,对于i<=sqrt(maxn),将i的每个倍数标记为合数
	{
		if(tf[i])continue;//如果i已经是合数,那么他的倍数肯定已经被标记过了,所以无需再次标记
		for(int j=2;i*j<=10000;j++)tf[i*j]=true;//如果是质数,进行标记操作
	}
	scanf("%d",&s);
	for(int i=s/2;i>=2;i--)if(!tf[i]&&!tf[s-i])//如果i和(s-i)都是质数
	{
		printf("%d\n",i*(s-i));
		return 0;//输出并返回
	}
}

02:大整数乘法

描述

求两个不超过200位的非负整数的积。

输入
有两行,每行是一个不超过200位的非负整数,没有多余的前导0。
输出
一行,即相乘后的结果。结果里不能有多余的前导0,即如果结果是342,那么就不能输出为0342。
样例输入
12345678900
98765432100
样例输出
1219326311126352690000
来源
程序设计实习2007

这题的数据有点水,不用FFT都可以过。。。但是我不会FFT

我写的代码是偏向于模板类型的(中了一点面向对象的毒),用了重载运算符

高精度的中心思想是通过类似于笔算的方法。比如说我们要计算200位+200位的加法,我们是从最低位(个位)开始,一步一步往上加,同时保存进位。

为了阅读和编写的方便,我没有用高精压位:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=410;
struct bigint
{
	int l,num[maxn];//l是大整数的长度,num保存每一位,num[0]是个位,依此类推
	bigint(){l=1;memset(num,0,sizeof num);}//初始化
	void in(){char s[maxn];scanf("%s",s);l=strlen(s);for(int i=0;i<l;i++)num[i]=s[l-i-1]-'0';}//输入函数
	void out(){for(int i=l-1;i>=0;i--)printf("%d",num[i]);printf("\n");}//输出函数,内置
	bigint operator+(bigint a)//加法操作
	{
		bigint ans;
		ans.l=max(l,a.l)+1;//更新长度
		for(int i=0;i<ans.l;i++)ans.num[i]=num[i]+a.num[i];//计算每一位的值
		for(int i=1;i<ans.l;i++)ans.num[i]+=ans.num[i-1]/10,ans.num[i-1]%=10;//计算进位
		if(!ans.num[ans.l-1])ans.l--;//判断最高位是否是0
		return ans;//返回
	}
	bigint move(int a)//移位操作,move(a)代表*(10^a)
	{
		bigint ans;
		ans.l=l+a;
		for(int i=0;i<l;i++)ans.num[i+a]=num[i];
		return ans;
	}
	bigint operator*(int a)//乘法操作,乘以一个小于10的数
	{
		bigint ans;
		ans.l=l+1;
		for(int i=0;i<ans.l;i++)ans.num[i]=num[i]*a;//计算
		for(int i=1;i<ans.l;i++)ans.num[i]+=ans.num[i-1]/10,ans.num[i-1]%=10;//进位
		if(!ans.num[ans.l-1])ans.l--;//判断最高位是否为0
		return ans;//返回
	}
	bigint operator*(bigint a)//乘法,乘以一个大整数
	{
		bigint ans;
		ans.l=l+a.l+1;//更新长度
		for(int i=0;i<l;i++)ans=ans+(a*num[i]).move(i);//对于每一位,加上乘以这一位的值
		while(ans.l!=1&&!ans.num[ans.l-1])ans.l--;//判断是否需要更新总长度
		return ans;//返回
	}
}a,b;
int main()
{
	a.in();b.in();//输入
	(a*b).out();//输出
	return 0;
}

03:字符环

描述

有两个由字符构成的环。请写一个程序,计算这两个字符环上最长连续公共字符串的长度。例如,字符串“ABCEFAGADEGKABUVKLM”的首尾连在一起,构成一个环;字符串“MADJKLUVKL”的首尾连在一起,构成一个另一个环;“UVKLMA”是这两个环的一个连续公共字符串。

输入
一行,包含两个字符串,分别对应一个字符环。这两个字符串之间用单个空格分开。字符串长度不超过255,且不包含空格等空白符。
输出
输出一个整数,表示这两个字符环上最长公共字符串的长度。
样例输入
ABCEFAGADEGKABUVKLM MADJKLUVKL
样例输出
6

用DP方法求两个字符串的最长连续公共字符串的长度的方法描述如下:定义f[i][j]表示以字符串1的第i位和字符串2的第j位为结尾的最长连续公共字符串的长度,可以得出状态转移方程为:f[i][j]=(s1[i]==s2[j]?f[i-1][j-1]+1:0)

求环上的最长连续公共字符串,使用倍增的思想,把两个字符串翻倍,例如AB翻倍为ABA,ABBCCCD翻倍为ABBCCCDABBCCC,然后再求最长连续公共字符串的长度。

注意:现在得出的答案还不是正确的答案,因为这样的做法可能会把一个元素使用两次。可以证明,如果存在着一个长度为n+1的最长连续公共字符串,那么一定存在一个长度为n的最长连续公共字符串,所以最后的答案就要输出min(ans,min(l1,l2))

复杂度分析:时间复杂度O(NM),空间复杂度O(NM),通过滚动数组法可以优化到O(min(N,M))(N,M是两个字符串的长度)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=610;
char s1[maxn],s2[maxn];
int l1,l2,L1,L2,f[maxn][maxn],ans=0;
int main()
{
	memset(f,0,sizeof f);
	scanf("%s%s",s1,s2);
	l1=strlen(s1);l2=strlen(s2);
	for(int i=0;i<l1-1;i++)s1[i+l1]=s1[i];
	for(int i=0;i<l2-1;i++)s2[i+l2]=s2[i];
	L1=l1*2-1;L2=l2*2-1;
	for(int i=0;i<L1;i++)for(int j=0;j<L2;j++)
	if(s1[i]==s2[j])f[i+1][j+1]=f[i][j]+1,ans=max(ans,f[i+1][j+1]);
	printf("%d\n",min(ans,min(l1,l2)));
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值