noip提高组解题报告C语言.docx
- 文档编号:6719707
- 上传时间:2023-01-09
- 格式:DOCX
- 页数:12
- 大小:22.06KB
noip提高组解题报告C语言.docx
《noip提高组解题报告C语言.docx》由会员分享,可在线阅读,更多相关《noip提高组解题报告C语言.docx(12页珍藏版)》请在冰豆网上搜索。
noip提高组解题报告C语言
1.潜伏者
(spy.pas/c/cpp)
【问题描述】
R国和S国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动。
历经艰险后,潜伏于S国的R国间谍小C终于摸清了S国军用密码的编码规则:
1、 S国军方内部欲发送的原信息经过加密后在网络上发送,原信息的内容与加密后所的内容均由大写字母‘A’—‘Z’构成(无空格等其他字母)。
2、 S国对于每个字母规定了对应的“密字”。
加密的过程就是将原信息中的所有字母替换为其对应的“密字”。
3、 每个字母只对应一个唯一的“密字”,不同的字母对应不同的“密字”。
“密字”可以和原字母相同。
例如,若规定‘A’的密字为‘A’,‘B’的密字为‘C’(其他字母及密字略),则原信息“ABA”被加密为“ACA”。
现在,小C通过内线掌握了S国网络上发送的一条加密信息及其对应的原信息。
小C希望能通过这条信息,破译S国的军用密码。
小C的破译过程是这样的:
扫描原信息,对于原信息中的字母x(代表任一大写字母),找到其在加密信息中的对应大写字母y,并认为在密码里y是x的密字。
如此进行下去直到停止于如下的某个状态:
1、 所有信息扫描完毕,‘A’—‘Z’所有26个字母在原信息中均出现过并获得了相应的“密字”。
2、 所有信息扫描完毕,但发现存在某个(或某些)字母在原信息中没有出现。
3、 扫描中发现掌握的信息里有明显的自相矛盾或错误(违反S过密码的编码规则)。
例如某条信息“XYZ”被翻译为“ABA”就违反了“不同字母对应不同密字”的规则。
在小C忙得头昏脑胀之际,R国司令部又发来电报,要求他翻译另外一条从S国刚刚截取到的加密信息。
现在请你帮助小C:
通过内线掌握的信息,尝试破译密码。
然后利用破译的密码,翻译电报中的加密信息。
【输入】
输入文件名为spy.in,共3行,每行为一个长度在1到100之间的字符串。
第1行为小C掌握的一条加密信息。
第2行为第1行的加密信息所对应的原信息。
第3行为R国司令部要求小C翻译的加密信息。
输入数据保证所有字符串仅由大写字母‘A’—‘Z’构成,且第1行长度与第2行相等。
【输出】
输出文件spy.out共1行。
若破译密码停止时出现2,3两种情况,请你输出“Failed”(不含引号,注意首字母大写,其它小写)。
否则请输出利用密码翻译电报中加密信息后得到的原信息。
【输入输出样例1】
spy.in
spy.out
AA
AB
EOWIE
Failed
【输入输出样例1说明】
原信息中的字母‘A’和‘B’对应相同的密字,输出“Failed”。
【输入输出样例2】
spy.in
spy.out
QWERTYUIOPLKJHGFDSAZXCVBN
ABCDEFGHIJKLMNOPQRSTUVWXY
DSLIEWO
Failed
【输入输出样例2说明】
字母‘Z’在原信息中没有出现,输出“Failed”。
【输入输出样例3】
spy.in
spy.out
MSRTZCJKPFLQYVAWBINXUEDGHOOILSMIJFRCOPPQCEUNYDUMPP
YIZSDWAHLNOVFUCERKJXQMGTBPPKOIYKANZWPLLVWMQJFGQYLL
FLSO
NOIP
【分析】注意每个限定条件就好了,小借哈希表存储,仅测试样例且通过。
【程序】
#include
#include"string.h"
charstr[101];
main()
{
voiddeciphering(charstr1[],charstr2[]);
voidtrans(chardic[]);
FILE*in;
inti;
charstr1[101],str2[101],str3[101];
in=fopen("spy.in","r");
fscanf(in,"%s%s%s",str1,str2,str);
fclose(in);
deciphering(str1,str2);
}
voiddeciphering(charstr1[],charstr2[])
{
chartemp1[26]={},temp2[26]={};
FILE*out;
intn,ch,i;
n=strlen(str1);
for(i=0;i { ch=str1[i]-65; if(temp1[ch]=='\0')temp1[ch]=str2[i]; if(str2[i]! =temp1[ch])break; ch=str2[i]-65; if(temp2[ch]=='\0')temp2[ch]=str1[i]; if(str1[i]! =temp2[ch])break; } for(ch=0;ch<26;ch++) if(temp1[ch]);else{i=0;break;}//如果有字母未被翻译 if(i==n) trans(temp1); else{ out=fopen("spy.out","w"); fprintf(out,"Failed"); fclose(out); } } voidtrans(chardic[]) { inti; chartr[101]={}; FILE*out; for(i=0;i { tr[i]=dic[str[i]-'A']; } out=fopen("spy.out","w"); fprintf(out,"%s",tr); fclose(out); } 2.Hankson的趣味题 (son.pas/c/cpp) 【问题描述】 Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。 现在,刚刚放学回家的Hankson正在思考一个有趣的问题。 今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。 现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的: 已知正整数a0,a1,b0,b1,设某未知正整数x满足: 1、x和a0的最大公约数是a1; 2、x和b0的最小公倍数是b1。 Hankson的“逆问题”就是求出满足条件的正整数x。 但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。 因此他转而开始考虑如何求解满足条件的x的个数。 请你帮助他编程求解这个问题。 【输入】 输入文件名为son.in。 第一行为一个正整数n,表示有n组输入数据。 接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。 输入数据保证a0能被a1整除,b1能被b0整除。 【输出】 输出文件son.out共n行。 每组输入数据的输出结果占一行,为一个整数。 对于每组数据: 若不存在这样的x,请输出0; 若存在这样的x,请输出满足条件的x的个数; 【输出输出样例】 son.in son.out 2 41196288 951371776 6 2 【说明】 第一组输入数据,x可以是9、18、36、72、144、288,共有6个。 第二组输入数据,x可以是48、1776,共有2个。 【数据范围】 对于50%的数据,保证有1≤a0,b1,b0,b1≤10000且n≤100。 对于100%的数据,保证有1≤a0,b1,b0,b1≤2,000,000,000且n≤2000。 【分析】感觉我的方法很简洁,相比网上许多大神的算法。 估计是哪里算错了…… 推导过程: x为待求数,依题意,b1÷b0=a;b1÷x=b;a0÷a1=a';x/a1=b',可推出b×b'=b1÷a1;容易知道b与a最大公约数为1,b'与a'最大公约数也为1;x可能的个数等于满足以上条件的bb'的对数。 仅测试样例且通过。 【程序】 #include main() { FILE*in,*out; inthankson(unsignedlongarr[4]); intcompare(unsignedlonga,unsignedlongb); intn,t; unsignedlongarr[4]; in=fopen("son.in","r"); out=fopen("son.out","w"); fscanf(in,"%d",&n); while(n--){ fscanf(in,"%d%d%d%d",arr,arr+1,arr+2,arr+3); t=hankson(arr); fprintf(out,"%d\n",t); } fclose(in); fclose(out); } inthankson(unsignedlongarr[4]) { unsignedlonga0=arr[0],a1=arr[1],b0=arr[2],b1=arr[3]; unsignedlonga,ap,b,bp,t; inti,s=0; a=b1/b0; ap=a0/a1; if(b1%a1! =0)return0; else{ t=b1/a1; for(i=1;i<=sqrt(t);i++)//刚刚发现这里有个问题当b=b'也满足时,s也加了两次; { if(t%i==0){ b=i;bp=t/i; if(compare(b,a)&&compare(bp,ap))s++; if(compare(bp,a)&&compare(b,ap))s++; } } returns; } } intcompare(unsignedlonga,unsignedlongb) { unsignedlongr; while(a%b) { r=a%b; a=b; b=r; } if(b==1)return1; elsereturn0; } 3.最优贸易 (trade.pas/c/cpp) 【问题描述】 C国有n个大城市和m条道路,每条道路连接这n个城市中的某两个城市。 任意两个城市之间最多只有一条道路直接相连。 这m条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。 C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。 但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。 商人阿龙来到C国旅游。 当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。 设C国n个城市的标号从1-n,阿龙决定从1号城市出发,并最终在n号城市结束自己的旅行。 在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有n个城市。 阿龙通过这样的贸易方式赚取旅费: 他会选择一个经过的城市迈入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球。 用赚取的差价当作旅费。 由于阿龙主要是来C国旅游,他决定这个贸易只进行最多一次。 当然,在赚不到差价的情况下它就无需进行贸易。 假设C国有5个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行。 双向箭头表示这条道路为双向通行。 (图略) 假设1~n号城市的水晶球价格分别为4,3,5,6,1。 阿龙可以选择如下一条线路: 1->2->3->5,并在2号城市以3的价格买入水晶球,在3号城市以5的价格卖出水晶球,赚取的旅费数为2。 阿龙也可以选择如下一条线路: 1->4->5->4->5,并在第1次到达5号城市时以1的价格买入水晶球,在第2次到达4号城市时以6的价格卖出水晶球,赚取的旅费数为5。 现在给出n个城市的水晶球价格,m条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。 请你告诉阿龙,他最多能赚钱多少旅费。 【输入】 输入文件名为trade.in。 第一行包含2个正整数n和m,中间用一个空格隔开,分别表示城市的数目和道路的数目。 第二行n个正整数,每两个正整数之间用一个空格隔开,按标号顺序分别表示这n个城市的商品价格。 接下来m行,每行有3个正整数,x,y,z,每两个整数之间用一个空格隔开。 如果z=1,表示这条道路是城市x到城市y之间的单向道路;如果z=2,表示这条道路为城市x和城市y之间的双向道路。 【输出】 输出文件trade.out共1行,包含1个整数,表示最多能赚取的旅费。 如果没有进行贸易,则输出0。 【输出输出样例】 trade.in trade.out 55 43651 121 141 232 351 452 5 【数据范围】 输入数据保证1号城市可以到达n号城市。 对于10%的数据,1≤n≤6。 对于30%的数据,1≤n≤100。 对于50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。 对于100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市水晶球价格≤100。 【吐槽】图论很差,而且这道题的数据范围实在! @#¥%……,网上说民间做法是SPFA,不太懂,尝试做了一遍,转晕了,也找不到C语言写的答案。 网上C语言的答案太少了,似乎高中那些年学PASCAL的居多,我已然看不懂也懒得了。 这道题,以后有时间再说。 数据结构用起来挺生疏的。 4.靶形数独 (sudoku.pas/c/cpp) 【问题描述】 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。 但普通的数独对他们来说都过于简单了,于是他们向Z博士请教,Z博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。 靶形数独的方格同普通数独一样,在9格宽×9格高的大九宫格中有9个3格宽×3格高的小九宫格(用粗黑色线隔开的)。 在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入1到9的数字。 每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。 但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。 (如图略) 上图具体的分值分布是: 最里面一格(黄色区域)为10分,黄色区域外面的一圈(红色区域)每个格子为9分,再外面一圈(蓝色区域)每个格子为8分,蓝色区域外面一圈(棕色区域)每个格子为7分,最外面一圈(白色区域)每个格子为6分,如上图所示。 比赛的要求是: 每个人必须完成一个给定的数独(每个给定数独有可能有不同的填法),而且要争取更高的总分数。 而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。 如图,在以下这个已经填完数字的靶形数独游戏中,总分为2829。 游戏规定,将以总分数的高低决出胜负。 由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。 【输入】 输入文件名为sudoku.in。 一共9行,每行9个整数(每个数都在0—9的范围内),表示一个尚未填满的数独方格,未填满的空格用“0”表示。 每两个数字之间用一个空格隔开。 【输出】 输出文件sudoku.out共1行。 输出可以得到的靶形数独的最高分数。 如果这个数独无解,则输出整数-1。 【输入输出样例1】 sudoku.in sudoku.out 700900001 100005900 000200080 005020003 000000648 413000000 007002090 201060804 080504012 2829 【输入输出样例2】 sudoku.in sudoku.out 000702453 900008000 740005010 195080000 070000025 030579108 000601000 060900001 000000006 2852 【数据范围】 40%的数据,数独中非0数的个数不少于30。 80%的数据,数独中非0数的个数不少于26。 100%的数据,数独中非0数的个数不少于24。 【分析】大家都说暴搜,暴搜最开心了。 递归填入每一个可以填的空,填满一次计算一次,取最大分值。 另外沿不同的方向搜通过的数据不等。 仅测试样例且通过。 【程序】 #include intarray[9][9],M=0; main() { voidsudoku(intarr[][],inta,intb); FILE*in,*out; intarr[9][9],i,j; in=fopen("sudoku.in","r"); for(i=0;i<9;i++) for(j=0;j<9;j++) {fscanf(in,"%d",&arr[i][j]); array[i][j]=arr[i][j]; if(arr[i][j]==0)array[i][j]=10;} sudoku(arr,0,0); out=fopen("sudoku.out","w"); fprintf(out,"%d",M); fclose(in); fclose(out); } voidsudoku(intarr[][9],inta,intb) { intbl,s,i,j,t=0; if(a==9){s=0; for(i=0;i<9;i++) for(j=0;j<9;j++) { i>4? (t=i-4): (t=4-i); j>4? (bl=j-4): (bl=4-j); if(t t=10-t;//以半径确定分值 s+=arr[i][j]*t;} if(s>M)M=s; } else{ if(arr[a][b]! =array[a][b]){ for(t=1;t<=9;t++){ bl=1; for(i=0;i if(t==arr[i][b]){bl=0;break;} if(bl) for(i=a+1;i<9;i++) if(t==array[i][b]){bl=0;break;} if(bl) for(i=0;i if(t==arr[a][i]){bl=0;break;} if(bl) for(i=b+1;i<9;i++) if(t==array[a][i]){bl=0;break;} if(bl) for(i=a/3*3;i for(j=b/3*3;j
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- noip 提高 解题 报告 语言