前缀判断
三部排序
错误票据
翻硬币
带分数
区间连号数
大数学家高斯有个好习惯:无论如何都要记日记。 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢? 高斯出生于:1777年4月30日。 在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日。 高斯获得博士学位的那天日记上标着:8113 请你算出高斯获得博士学位的年月日。
输出格式
提交答案的格式是:yyyy-mm-dd, 例如:1980-03-21
请严格按照格式,通过浏览器提交答案。 注意:只提交这个日期,不要写其它附加内容,比如:说明性的文字。 1799 7 16
本体暴力数日期,然后做日期变更,属实体力活。
我们需要判定的点有“大月”、“小月”、“闰年”。
循环模拟过程即可,还需要注意年份的增加。
#include
using namespace std;
bool leap(int y)//闰年判断
{
return (y%400==0||y%100!=0&&y%4==0);
}
int main()
{
int y=1777,m=4,d=30;
for(int i=0;i<8113-1;i++)
{
d++;
if(m==12&&d==32)//第二年第一天
{
y++;
m=1;
d=1;
continue;
}
if((m==1||m==3||m==5||m==7||m==8||m==10)&&d==32)//大月
{
m++;
d=1;
continue;
}
if((m==4||m==6||m==9||m==11)&&d==31)//小月
{
m++;
d=1;
continue;
}
if(m==2&&!leap(y)&&d==29)//非闰年
{
m++;
d=1;
continue;
}
if(m==2&&leap(y)&&d==30)//闰年
{
m++;
d=1;
continue;
}
}
cout<<y<<" "<<m<<" "<<d;
return 0;
}
小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。
有一次,老师出的题目是:36 x 495 = ?
他却给抄成了:396 x 45 = ?
但结果却很戏剧性,他的答案竟然是对的!!
因为 36 * 495 = 396 * 45 = 17820
类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54
假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)
能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?
请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。
满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。
答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。
142
我们只需要满足条件: ab * cde = adb * ce
因为数据范围小,所以直接暴力枚举
#include
int main() {
int num = 0;
for (int a = 1; a < 10; ++a) {
for (int b = 1; b < 10; ++b) {
if (b != a)
for (int c = 1; c < 10; ++c) {
if (c != b && c != a)
for (int d = 1; d < 10; ++d) {
if (d != c && d != b && d != a)
for (int e = 1; e < 10; ++e) {
if (e != d && e != c && e != b && e != a)
if ((a * 10 + b)*(c * 100 + d * 10 + e) == (a * 100 + d * 10 + b)*(c * 10 + e))
num++;
}
}
}
}
}
std::cout << num;
}
小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!站在台阶前,他突然又想着一个问题:
如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢? 请你利用计算机的优势,帮助小明寻找答案。
要求提交的是一个整数。
1.我们可以从39步走完了去考虑,则最后一步确定,思考前一步(f[n-1]||f[n-2]),这样想可以想想为动态规划问题。
2.直接利用数据优势,暴搜拿出答案。
dfs
#include
using namespace std;
int ans;
void dfs(int step,int n)//step剩下得台阶数,n已经走的步数
{
if(step<0)return;
if(step==0&&n%2==0)ans++;//满足偶数条件且走完了则+1
dfs(step-2,n-1);//一次走一个还是两个,全部列出
dfs(step-1,n-1);
}
int main()
{
dfs(39,0);//从剩39级台阶往下走
cout<<ans;
return 0;
}
标题:黄金连分数
黄金分割数0.61803… 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。
对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!
言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。
比较简单的一种是用连分数:
1 黄金数 = --------------------- 1 1 + ----------------- 1 1 + ------------- 1 1 + --------- 1 + ...
这个连分数计算的“层数”越多,它的值越接近黄金分割数。
请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。
小数点后3位的值为:0.618
小数点后4位的值为:0.6180
小数点后5位的值为:0.61803
小数点后7位的值为:0.6180340
(注意尾部的0,不能忽略)
你的任务是:写出精确到小数点后100位精度的黄金分割值。
注意:尾数的四舍五入! 尾数是0也要保留!
显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。
黄金连分数其实就是黄金比例,当fib[]有极限时,fib[n-1]/fib[n]就是黄金比,约等于 (根号5-1)/2
我们可以按照上面的那个公式先写几项:1/2,2/3,3/5,5/8,8/13……可以看出其实就是相邻两项斐波那契数相除。
可以用斐波纳契数列和模拟手算除法实现
为什么取到48 和 49 我本来以为是爆int 发现49就爆了,所以我真不知道
0.6180339887498948481971959525508621220510663574518538453723187601229582821971784348083863296133320592
#include
using namespace std;
typedef long long LL;
void div(LL a,LL b,int end,int begin)//模拟手工除法
{
if(begin>end)return ;
int tmpans=a/b;
cout<<tmpans;
div((a%b)*10,b,end,begin+1);
}
int main()
{
unsigned long long f[500]={
0,1};
for (int i = 2; i<100; i++)f[i] = f[i - 1] + f[i - 2];
div(f[48],f[49],100,0);
return 0;
}
如下的代码判断 needle_start指向的串是否为haystack_start指向的串的前缀,如不是,则返回NULL。
比如:“abcd1234” 就包含了 “abc” 为前缀
char* prefix(char* haystack_start, char* needle_start)
{
char* haystack = haystack_start;
char* needle = needle_start;
while(*haystack && *needle){
if(______________________________) return NULL; //填空位置
}
if(*needle) return NULL;
return haystack_start;
}
请分析代码逻辑,并推测划线处的代码,通过网页提交。
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!
while(*haystack && *needle){
的指针用法是表明这两个部分都没有越出字符串的边界。
if(*needle) return NULL;
这里是判断如果不是前缀,因为注意比较之后neddle仍有剩余,所以不是 ,返回NULL。
基于以上的原则我们发现代码中明显缺少比对部分,且如果两个字符串不相等依然可以判定不是前缀。
则可以表明有两个推断1.haystack++与needle++
,以及2.haystack!= needle
。
结合来看即为(haystack++) != *(needle++)
*(haystack++) != *(needle++)
一般的排序有许多经典算法,如快速排序、希尔排序等。
但实际应用时,经常会或多或少有一些特殊的要求。我们没必要套用那些经典算法,可以根据实际情况建立更好的解法。
比如,对一个整型数组中的数字进行分类排序:
使得负数都靠左端,正数都靠右端,0在中部。注意问题的特点是:负数区域和正数区域内并不要求有序。可以利用这个特点通过1次线性扫描就结束战斗!!
以下的程序实现了该目标。
其中x指向待排序的整型数组,len是数组的长度。
void sort3p(int* x, int len)
{
int p = 0;
int left = 0;
int right = len-1;
while(p<=right){
if(x[p]<0){
int t = x[left];
x[left] = x[p];
x[p] = t;
left++;
p++;
}
else if(x[p]>0){
int t = x[right];
x[right] = x[p];
x[p] = t;
right--;
}
else{
__________________________; //填空位置
}
}
}
C++
25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0
-3,-2,-16,-5,0,0,0,21,19,33,25,16,18,25
请分析代码逻辑,并推测划线处的代码,通过网页提交
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!
p++;
负数都靠左端,正数都靠右端,0在中部。
某涉密单位下发了某种票据,并要在年终全部收回。
每张票据有唯一的ID号。
全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。
因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。
你的任务是通过编程,找出断号的ID和重号的ID。
假设断号不可能发生在最大和最小号。
第一行包含整数 N,表示后面共有 N 行数据。
接下来 N 行,每行包含空格分开的若干个(不大于100个)正整数(不大于100000),每个整数代表一个ID号。
要求程序输出1行,含两个整数 m,n,用空格分隔。
其中,m表示断号ID,n表示重号ID。
1≤N≤100
输入样例:
2
5 6 8 11 9
10 12 9
7 9
看题目本身只是在数组当中找出重号和缺号,我们只需要排序一下就行了。
难点在于字符串的读入处理上面。
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int a[N];
int n;
int main()
{
int cnt;
cin>>cnt;
string line;
getline(cin,line);
while(cnt--)// 忽略掉第一行的回车
{
getline(cin,line);//读入一行
stringstream ssin(line);//给入ssin
while(ssin>>a[n])n++;//放入数组
}
sort(a,a+n);
int an1,an2;
for(int i=1;i<=n;i++)
{
if(a[i]-a[i-1]>=2)an1=a[i]-1;// 断号
else if(a[i]-a[i-1]==0)an2=a[i];// 重号
}
cout<<an1<<" "<<an2;
return 0;
}
利用桶排序的思想,记录所有数组出现的次数。
#include
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int cnt;
cin>>cnt;
int t;
int maxv=-N,minv=N;
while(cnt--)
{
while(cin>>t)
{
a[t]++;
maxv=max(maxv,t);
minv=min(minv,t);
}
}
int an1,an2;
for(int i=minv;i<=maxv;i++)
if(!a[i])an1=i;
else if(a[i]==2)an2=i;
cout<<an1<<" "<<an2;
return 0;
}
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:oo*oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作。
两行等长的字符串,分别表示初始状态和要达到的目标状态。
一个整数,表示最小操作步数
输入字符串的长度均不超过100。
数据保证答案一定有解。
**********
o****o****
5
*o**o***o***
*o***o**o***
1
当前第i个硬币的状态被i-1所决定。
当我们有任意两组硬币时,只能选择同时翻动i,i+1,于是我们连续翻动,知道数组a完全等于数组b则输出次数即可。
#include
#include
using namespace std;
string a,b;//读取字符串a,b,且其实字符串为a
int cnt;//计数
void turn(int u)//反转硬币
{
if(a[u]=='*')a[u]='o';
else a[u]='*';
}
int main()
{
cin>>a>>b;
for(int i=0;i<=a.size()-1;i++)if(a[i]!=b[i])turn(i),turn(i+1),cnt++;//因为a,b长度相等所以不管以哪一个为长度终点都没有问题,且如果a[i]与b[i]不相等,则反转两颗硬币,并次数加一。
cout<<cnt;
return 0;
}
100 可以表示为带分数的形式:100=3+69258714
还可以表示为:100=82+3546197
注意特征:带分数中,数字 1∼9 分别出现且只出现一次(不包含 0)。
类似这样的带分数,100 有 11 种表示法。
一个正整数。
输出输入数字用数码 1∼9 不重复不遗漏地组成带分数表示的全部种数。
1≤N<106
100
11
105
6
有题可知带分数中,数字 1∼9 分别出现且只出现一次
且带分数的特性为n=a+b/c
,换言之即为n*c=a*c+b;
因为最耿直的做法我们可以将1-10直接暴搜且限制每个数字只出现一次,然后判断是否符合n*c=a*c+b;
即可。
#include
using namespace std;
const int N=1e6+10;
bool st[N];//判断当前数是否被使用
int n,num[N],cnt;
int sum(int l,int r)//取出num中存储的数值,计算a,b,c的数值
{
int t=0;
for(int i=l;i<=r;i++)
t=t*10+num[i];
return t;
}
void dfs(int u)
{
if(u>9)//如果1到9放置完毕
{
for(int i=1;i<=9;i++)
for(int j=i+1;j<=9;j++)//用二维遍历所有的长度可能性
{
int a=sum(1,i);
int b=sum(i+1,j);
int c=sum(j+1,9);//a,b,c的长度被分成三段限制
if(n*c==a*c+b)cnt++;//如果满足判断条件则加1
}
return ;
}
for(int i=1;i<=9;i++)//暴搜1-9的排列
{
if(!st[i])//如果当前数字没有被选择
{
st[i]=true;//选择,下一次不能再被使用
num[u]=i;//放入数组num
dfs(u+1);//判断下一个数字
num[u]=0;
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(1);//从1开始跑,绝对够直白
cout<<cnt;//输出有多少种满足条件的带分数
return 0;
}
小明这些天一直在思考这样一个奇怪而有趣的问题:
在 1∼N 的某个排列中有多少个连号区间呢?
这里所说的连号区间的定义是:
如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。
当 N 很小的时候,小明可以很快地算出答案,但是当 N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
第一行是一个正整数 N,表示排列的规模。
第二行是 N 个不同的数字 Pi,表示这 N 个数字的某一排列。
输出一个整数,表示不同连号区间的数目。
1≤N≤10000,
1≤Pi≤N
4
3 2 4 1
7
5
3 4 2 5 1
9
第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]
第二个用例中,有 9 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[1,5],[2,2],[3,3],[4,4],[5,5]
如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。
4
3 2 4 1
第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]
1.因为连号必须满足 A.单调递增子序列 B.自身
2.因为 任意子序列中 虽有的数字必须被使用完 才能表明符合单调递增子序列
3.模拟成 左右两个端点循环 当右端点-左端点 等于 当前最大值 - 当前最小值
则表明 从左端点到右端点的所有数都被使用 是单调递增子序列 (且 该情况对a[i] - a[i] 同样适用
则答案+1
枚举所有区间,如果最大值减最小值等于区间长度,则表明用光了所有数据且是连号,则是连号区间
#include
using namespace std;
const int N=1e4+10,INF=1e8;
int a[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int cnt=0;
for(int i=1;i<=n;i++)
{
int maxv=-INF,minv=INF;
for(int j=i;j<=n;j++)
{
maxv=max(a[j],maxv),minv=min(a[j],minv);
if(maxv-minv==j-i)cnt++;
}
}
cout<<cnt;
return 0;
}