HFOI2017.9.12 复习赛01题解

来源:互联网 发布:js modal点击旁边关闭 编辑:程序博客网 时间:2024/06/07 06:12

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。
样例输入
1234567890098765432100
样例输出
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--;//判断最高位是否是0return 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--;//判断最高位是否为0return 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;}


原创粉丝点击