声明:作者水平有限,只是会基础C语言的小菜,C++还未入门。作者仅根据算法竞赛入门经典(第二版)书上第三章习题所述题意而编写,并未严格按照原题的输入输出编写,代码仅经过个人测试(OJ网站太慢了)。代码、文章排版及书写如有错误,欢迎指正!

目录

声明:作者水平有限,只是会C语言的小菜,C++还未入门。作者仅根据算法竞赛入门经典(第二版)书上第三章习题所述题意而编写,并未严格按照原题的输入输出编写,程序仅经过个人测试,如有错误,欢迎指正!

习题3-1 得分(Score,ACM/ICPC Seoul 2005,UVa1585)

习题3-2 分子量(Molar Mass,ACM/ICPC Seoul 2007,UVa1586)

习题3-3 数数字(Digit Counting,ACM/ICPC Danang 2007,UVa1225)

习题3-4 周期串(Periodic Strings,UVa455)

习题3-5 谜题(Puzzle,ACM/ICPC World Finals 1993,UVa227)

习题3-6 纵横字谜的答案(Crossword Answers,ACM/ICPC World Finals 1994,UVa232)

习题3-7 DNA序列(DNA Consensus String,ACM/ICPC Seoul 2006,UVa1368)

习题3-8 循环小数(Repeating Decimals,ACM/ICPC World Finals 1990,UVa202)

习题3-9 子序列(All in All,UVa10340)

习题3-10 盒子(Box,ACM/ICPC NEERC 2004,UVa1587)

习题3-11 换抵挡装置(Kickdown,ACM/ICPC NEERC 2006,UVa1588)

习题3-12  浮点数(Floating-Point Numbers,UVa11809)


习题3-1 得分(Score,ACM/ICPC Seoul 2005,UVa1585)

        给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。

#include <stdio.h>
#include <string.h>int main()
{char ox[100];while(scanf("%s", ox) == 1){int sum = 0, t = 0;for(int i = 0; i < strlen(ox); i++){if(ox[i] == 'O')t++;else if(ox[i] == 'X')t = 0;sum += t;}printf("%d\n", sum);}return 0;
} 

习题3-2 分子量(Molar Mass,ACM/ICPC Seoul 2007,UVa1586)

        给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分别为C,H,O,N,原子量分贝为12.01,1.008,16.00,14.01(单位:g/mol)。例如,C6H5OH的分子量为94.108g/mol。

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>#define c 12.01
#define h 1.008
#define o 16.00
#define n 14.01int i;   //全局变量 //判断元素数字下标
int subscript(char *a)
{int t = 0, s = 0;while(isdigit(a[++i]))    //判断元素符号后面的数字个数 t++;if(t == 0)return 1;for(; t > 0; t--)s += (a[i-t] - '0')*pow(10,t-1); //转化 return s;
} int main()
{char mf[100];while(scanf("%s", mf) == 1){float sum = 0;for(i = 0; i < strlen(mf);){switch(mf[i]){case 'C':sum += c*subscript(mf);break;case 'H':sum += h*subscript(mf);break;case 'O':sum += o*subscript(mf);break;case 'N':sum += n*subscript(mf);break;}}printf("%.3fg/mol\n", sum);  //保留三位小数 }return 0;
} 

注意:个人认为分子式中元素符号后有多位数的情况不能忽略。

习题3-3 数数字(Digit Counting,ACM/ICPC Danang 2007,UVa1225

把前n(n≤10000)个整数顺次写在一起:123456789101112...数一数0~9各出现多少次(输出10个整数,分别是0,1, ... ,9出现的次数)。

#include <stdio.h>
#include <string.h>int main()
{char a[10000];while(scanf("%s", a) == 1){int num[10] = {};    //数组初始化 for(int i = 0; i < strlen(a); i++)num[a[i] - '0']++;  //对应数组项计数 for(int i = 0; i < 10; i++)printf("%5d", num[i]);printf("\n");}return 0;
} 

习题3-4 周期串(Periodic Strings,UVa455)

        如果一个字符串可以由某个长度为k的字符串重复多次得到,则称该串以k为周期。例如,abcabcabcabc以3为周期(注意,它也以6和12为周期)。

输入一个长度不超过80的字符串,输出其最小周期。

#include <stdio.h>
#include <string.h> int main()
{ char string1[100]; while(scanf("%s", string1) == 1){char string2[85];         for(int k = 1; k <= strlen(string1); k++){strncpy(string2,string1,k);    //从首部开始截取字符串,长度为kint i = 0;for(; i < strlen(string1); i++)if(string1[i] != string2[i % k])    //遍历字符串判断是否周期循环 break;  if(i >= strlen(string1) && strlen(string1) % k == 0){//printf("%s\n", string2);printf("%d\n",k);break;}}}return 0;
} 

注:C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

习题3-5 谜题(Puzzle,ACM/ICPC World Finals 1993,UVa227)

有一个5*5的网格,其中恰好有一个格子是空的,其他格子各有一个字母。一共有4种指令:A,B,L,R,分别表示把空格上、下、左右的相邻字母移到空格种。输入初始网格和指令序列(以数字0结束),输出指令执行完毕后的网格。如果有非法指令,应输出“This puzzle has no final configuration.”,例如,图3-5种执行ARRBBL0后,效果如图3-6所示。

#include <stdio.h>
#include <string.h>int main()
{char a[30];scanf("%[^\n]", a);   //读取除回车以外的字符 fflush(stdin); //清空输入缓冲区,确保不影响后面的数据读取 char grid[5][5];for(int i = 0, k = 0; i < 5; i++)for(int j = 0; j < 5; j++)  grid[i][j] = a[k++];/*for(int i = 0; i < 5; i++)   //打印输入网格 {for(int j = 0; j < 5; j++)  printf("%c", grid[i][j]);printf("\n");}*/char *ptr;int x, y;ptr = strchr(a, ' ');    //寻找空格位置x = (ptr - a) / 5, y = (ptr - a) % 5; //printf("空格的坐标为(%d,%d)\n", x, y);char cmd[100];scanf("%[^0]", cmd);    //读取除'0'以外的字符 //printf("%s\n", cmd);for(int i = 0; i < strlen(cmd); i++){switch(cmd[i]){case 'A':grid[x][y] = grid[x - 1][y];grid[x - 1][y] = ' ';x = x - 1;break;case 'B':grid[x][y] = grid[x + 1][y];grid[x + 1][y] = ' ';x = x + 1;break;case 'L':grid[x][y] = grid[x][y - 1];grid[x][y - 1] = ' ';y = y - 1;break;    case 'R':grid[x][y] = grid[x][y + 1];grid[x][y + 1] = ' ';y = y + 1;break;default:printf("This puzzle has no final configuration.\n");return 0;}}for(int i = 0; i < 5; i++){for(int j = 0; j < 5; j++)  printf("%c", grid[i][j]);printf("\n");}     return 0;
}

习题3-6 纵横字谜的答案(Crossword Answers,ACM/ICPC World Finals 1994,UVa232)

       输入一个r行c列(1≤r,c≤10)的网格,黑格用“*”表示,每个白格都填有一个字母。如果一个白格的左边相邻位置或者上边相邻位置没有白格(可能是黑格,也可能出了网格边界),则称这个白格是一个起始格。

首先把所有起始格按照从上到下、从左到右的顺序编号为1, 2, 3,…,如图3-7所示。

接下来要找出所有横向单词(Across)。这些单词必须从一个起始格开始,向右延伸到一个黑格的左边或者整个网格的最右列。最后找出所有竖向单词(Down)。这些单词必须从一个起始格开始,向下延伸到一个黑格的上边或者整个网格的最下行。输入输出格式和样例请参考原题。

(哈哈,题目没怎么看懂,也不想看懂,找了个其他作者写的代码)请参考:

#include<stdio.h>
#include<string.h>
#define m 105
char s[m][m];
int sta[m][m];
int main(){int r,c,kase=0;while(scanf("%d%d\n",&r,&c)==2&&r){memset(sta,0,sizeof(sta));int pos=1;for(int i=0;i<r;i++){for(int j=0;j<c;j++){scanf("%c",&s[i][j]);if(s[i][j]=='*')continue;if(j<1||s[i][j-1]=='*'||i<1||s[i-1][j]=='*'){sta[i][j]=pos;pos++;}}getchar();}kase++;if(kase>1)printf("\n");printf("puzzle #%d:\n",kase);printf("Across\n");for(int i=0;i<r;i++){int j=0;while(j<c){if(s[i][j]=='*'||sta[i][j]==0){j++;continue;}printf("%3d.%c",sta[i][j],s[i][j]);j++;while(s[i][j]!='*'&&j<c){printf("%c",s[i][j]);j++;}printf("\n");}}printf("Down\n");for(int i=0;i<r;i++){for(int j=0;j<c;j++){if(s[i][j]=='*'||sta[i][j]==0)continue;printf("%3d.%c",sta[i][j],s[i][j]);sta[i][j]=0;int k=i+1;while(k<r&&s[k][j]!='*'){printf("%c",s[k][j]);sta[k][j]=0;k++;}printf("\n");  }}  }return 0;
} 

注:原文链接:算法竞赛入门经典(第二版)习题答案——第三章3.1-3.6_qq_45911039的博客-CSDN博客

习题3-7 DNA序列(DNA Consensus String,ACM/ICPC Seoul 2006,UVa1368)

输入m个长度均为n的DNA序列,求一个DNA序列,到所有序列的总Hamming距离尽量小。两个等长祖父穿的Hamming距离等于字符不同的位置个数,例如,ACGT和GCGA的Hamming距离为2(左数第1,4个字符不同)。

输入整数m和n(4≤m≤50,4≤n≤1000),以及m个长度为n的DNA序列(只包含字母A,C,G,T),输出到m个序列的Hamming距离和最小的DNA序列和对应的距离。如有多解,要求为字典序最小的解。例如,对于下面5个DNA序列,最优解为TAAGATAC。

TATGATAC

TAAGCTAC

AAAGATCC

TGAGATAC

TAAGATGT

#include <stdio.h>int main()
{int T;scanf("%d", &T);while(T--){int m, n;scanf("%d%d", &m, &n);char DNA[m][n + 1];for(int i = 0; i < m; i++)scanf("%s", DNA[i]);int a[n] = {0}, c[n] = {0}, g[n] = {0}, t[n] = {0},  //用于计数 max[n] = {0}, distance[n] = {0}, distance_sum = 0;char solution[n + 1];for(int j = 0; j < n; j++)  //每一列字符计数,众数即为最优解所在列的字符 {for(int i = 0; i < m; i++)switch(DNA[i][j]){case 'A':a[j]++; break;case 'C':c[j]++; break;case 'G':g[j]++; break;case 'T':t[j]++; break;}max[j] = a[j], solution[j] = 'A', distance[j] = c[j] + g[j] + t[j];        if(c[j] > max[j])   //比较中隐藏了按字典序比较,顺序比较即可 max[j] = c[j], solution[j] = 'C', distance[j] = a[j] + g[j] + t[j];if(g[j] > max[j])     //按列除最优解字符外相加即为距离max[j] = g[j], solution[j] = 'G', distance[j] = a[j] + c[j] + t[j];if(t[j] > max[j])max[j] = t[j], solution[j] = 'T', distance[j] = a[j] + c[j] + g[j];}solution[n] = '\0';    //添加字符串末尾结束标志 printf("到m个序列的Hamming距离和最小的DNA序列:%s\n", solution);for(int j = 0; j < n; j++)distance_sum += distance[j]; //距离和 printf("对应的距离:%d\n", distance_sum); }return 0;
}

注:一开始审题错了,产生歧义,以为从m个序列之中寻找最优解,其实是从m个序列之外寻找最优解。

附(从m个序列之中寻找最优解):

//审题产生歧义,以为从m个序列之中寻找最优解
#include <stdio.h>
#include <string.h>int main()
{int m, n;scanf("%d%d", &m, &n);char DNA[m][n + 1];for(int i = 0; i < m; i++)scanf("%s", DNA[i]);int a[m][m];memset(a, 0, sizeof(a));    //确保初始化为0 for(int i = 0; i < m; i++) for(int j = 0; j < m; j++){if(i == j)continue;for(int k = 0; k < n; k++)if(DNA[i][k] != DNA[j][k])a[i][j]++;}/*for(int i = 0; i < m; i++)for(int j = 0; j < m; j++)printf("a[%d][%d] = %d\n", i, j, a[i][j]);*/int sum[m] = {0}, min = 100, t;for(int i = 0; i < m; i++){for(int j = 0; j < m; j++)sum[i] += a[i][j];if(sum[i] < min){t = i;min = sum[i];}else if(sum[i] == min)if(strcmp(DNA[t], DNA[i]) > 0){t = i;min = sum[i];}elsecontinue;}printf("到m个序列的Hamming距离和最小的DNA序列:%s\n", DNA[t]);for(int i = 0; i < m; i++)printf("它对应到第%d个序列的距离:%d\n", i, a[t][i]);return 0;
} 

习题3-8 循环小数(Repeating Decimals,ACM/ICPC World Finals 1990,UVa202)

输入整数a和b(0≤a≤3000,1≤b≤3000),输出a/b的循环小数表示以及循环解长度。例如a=5,b=43,小数表示为0.(116279069767441860465),循环节长度为21。

这道题主要就是小数部分的处理,想转化为字符串处理,发现还是有点复杂,还是直接参考其他作者的代码,这个是我认为写的比较清晰简洁的,请参考:

#include<stdio.h>int res[3005], mod[3005]; //res数组记录每一次的商,mod数组记录每一次的余数
int main()
{int a, b;while(scanf("%d%d", &a, &b) != EOF){int i = 0, j;int flag = 0;   //标记是否找到了循环节 printf("%d/%d = %d.", a, b, a/b);   //先输出整数部分 while(true){res[i]= a / b;mod[i]= a % b;a=(a % b) * 10; //将这次的余数乘以十作为下一次的被除数 for(j = 1; j < i; j++)  //与前面出现的商和余数进行比较;注意是从j=1,即小数点后进行比较 if(res[j] == res[i] && mod[j] == mod[i]){    //若余数和商的组合在前面出现过,则表示出现循环节 flag = 1; //此时的i为第二次出现循环节的开头第一个数字的位置 break;}if(flag)break;i++;}for(int k = 1; k < j; k++) //注意到res[0]中存储的是整数部分 printf("%d", res[k]);    //输出小数点后非循环节部分 printf("(");if(i > 50){ //如果要输出的小数部分位数大于50,就只输出前50个 for(int k = j; k <= 50; k++)printf("%d", res[k]);printf("...)\n");}else{for(int k = j; k < i; k++)printf("%d", res[k]);printf(")\n");}printf("   %d = number of digits in repeating cycle\n\n", i-j); //注意输出格式,循环节长度前有三个空格,末尾两个换行符 }return 0;
}

注:原文链接:【算法竞赛入门经典】习题3-8循环小数(Uva202)_GUANYX的博客-CSDN博客

习题3-9 子序列(All in All,UVa10340)

输入两个字符串s和t,判断是否可以从t中删除0个或多个字符(其他字符顺序不变),得到字符串s。例如,abcde可以得到bce,但无法得到dc。

粗看题目,似乎略复杂,其实不然,我们用不着删除字符,只需顺序遍历两个序列即可。

#include <stdio.h>
#include <string.h> int main()
{ char s[10000], t[10000];while(scanf("%s%s", s, t) != EOF){int i, j = 0;for(i = 0 ; i < strlen(t); i++)if(t[i] == s[j])    //顺序寻找字符串t中是否含有字符串s中的字符即可 j++;if(j >= strlen(s))  //字符串s遍历完全即符合 printf("Yes\n");elseprintf("No\n");}return 0;
} 

刚开始,注意力集中在字符串t其内寻找的s字符串中的字符顺序一定要越来越后,便想到用strchr函数和strncpy函数结合,找到就截取后面的字符串再找。后来想到,这不就是顺序遍历两个字符串嘛,自加就可以了哈哈。

字符串处理函数版:

#include <stdio.h>
#include <string.h>int main()
{char s[10000], t[10000];while(scanf("%s%s", s, t) != EOF){char *ptr = 0, c, a[10000];strcpy(a, t); //考虑到字符串t需要截取,拷贝字符串t给字符串a int i;for(i = 0; i < strlen(s); i++){c = s[i];ptr = strchr(a, c);  //顺序寻找字符串s中字符c在字符串a(t)第一次出现的位置 if (ptr) strncpy(a, ptr + 1, strlen(a) - (ptr - a));  //截取字符串a中字符c后面的字符串 else{printf("No\n");break;}}if(i >= strlen(s))printf("Yes\n");}return 0;
} 

注:C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

习题3-10 盒子(Box,ACM/ICPC NEERC 2004,UVa1587)

给定6个矩形的长和宽wi和hi(1≤wi,hi≤10000),判断它们能否构成长方体的6个面。

思路:

第一种:正方体(aa=6),只出现一条边a
第二种:侧面相等的长方体(ab == 4 && aa == 2),只出现两条边a,b
第三种:一般长方体(ab == 2 && ac == 2 & bc == 2),只出现三条边a,b,c

注意:按构成长方体的矩形面划分只有这三种情况

(这种思路好像不太灵活,但是我觉得是比较容易理解的,暂时没有其他思路)

#include <stdio.h>struct Rectangle
{int h;int w;
}rect[6];   //定义矩形结构体数组 int main()
{int T;                 scanf("%d", &T);   while(T--){for(int i = 0; i < 6; i++)scanf("%d%d", &rect[i].h, &rect[i].w);int aa = 0, bb = 0, ab = 0, ac = 0, bc = 0;int a = rect[0].h, b = rect[0].w, c, flag = 0;   //将第一组数据设为a和b,flag用于判断c是否出现过 for(int i = 0; i < 6; i++){       //下面的判断语句就是边长a,b,c的组合问题,稍微比划两下就很明了 if(rect[i].h == a && rect[i].w == a)  aa++;else if(rect[i].h == b && rect[i].w == b)bb++;else if(rect[i].h == a && rect[i].w == b || rect[i].h == b && rect[i].w == a)ab++;else if(rect[i].h == a && rect[i].w != b && !flag){c = rect[i].w;flag = 1; ac++;}else if(rect[i].h == b && rect[i].w != a && !flag){c = rect[i].w;flag = 1;bc++;}else if(rect[i].w == a && rect[i].h != b && !flag){c = rect[i].h;flag = 1;ac++;    }else if(rect[i].w == b && rect[i].h != a && !flag){c = rect[i].h;flag = 1;bc++; }else if(rect[i].h == a && rect[i].w == c || rect[i].h == c && rect[i].w == a)ac++;else if(rect[i].h == b && rect[i].w == c || rect[i].h == c && rect[i].w == b)bc++;elsebreak; //其他情况说明出现第四条边,直接退出      }//printf("aa=%d,bb=%d,ab=%d,ac=%d,bc=%d\n", aa, bb, ab, ac, bc);if(aa == 6 || (ab == 4 && aa == 2) || (ab == 4 && bb == 2) || (ab == 2 && ac == 2 & bc == 2)) printf("POSSIBLE\n");    //符合以上条件的6组数据则可以构成长方体 elseprintf("IMPOSSIBLE\n"); }return 0;
}

习题3-11 换抵挡装置(Kickdown,ACM/ICPC NEERC 2006,UVa1588)

给出两个长度分别为n1, n2(n1,n2≤100)且每列高度只为1或2的长条。需要将它们放入一个高度为3的容器(如图3-8所示),问能够容纳它们的最短容器长度。

(书上没有写输入输出格式,网上找的)

Sample Input                    
2112112112

2212112

12121212

21212121

2211221122

21212

Sample Output

10

8

5

思路:一开始没有输入输出有点懵,想了一下可以将长条数字化 ,果然是这样的。思路就是固定一个长条,先得清楚哪个长条更长,哪个更短,两个长条完全接触的时候长的便是最短容器长度;移动一个长条,计算每一个接触的地方累加的高度是否符合题意。需要注意的是,长条需要完全在固定长条的左端开始移动,否则会漏掉符合的长条移动位置。

#include <stdio.h>
#include <string.h>int main()
{ char s1[105], s2[105], a[105], b[105];while(scanf("%s%s", s1, s2) != EOF){int n1 , n2 ;if(strlen(s1) <= strlen(s2))    //固定长度最小的长条为a,长度为n1,拷贝给a strcpy(a, s1), strcpy(b, s2), n1 = strlen(s1), n2 = strlen(s2);elsestrcpy(a, s2), strcpy(b, s1), n1 = strlen(s2), n2 = strlen(s1);int n, min = n1 + n2;   //b从a左端开始移动,每次移动一个单位长度,n为移动次数,设置min初始值为两长条长度之和 for(n = 0; n < n1 + n2; n++){int i = 0;if(n <= n1) //b最右端未超出a {for(i = 0; i < n; i++)if((a[i] - '0' + b[n2 - n + i] - '0') > 3)break;if(i >= n && n1 + n2 -n < min)min = n1 + n2 -n;  //最短容器长度等于长条a和b的总长减去接触部分(b的移动次数n) }else if(n > n1 && n <= n2)  //b最左端未超出a {for(i = 0; i < n1; i++)if((a[i] - '0' + b[n2 - n + i] - '0') > 3)break;if(i >= n1 && n2 < min)min = n2;  //最短容器长度等于较长的那根长条(b)长度 (n2) }else   //b最左端超出a {for(i = n - n2; i < n1; i++)if((a[i] - '0' + b[n2 - n + i] - '0') > 3)break;if(i >= n1 && n < min)min = n;    //最短容器长度等于b的移动次数 }}printf("%d\n", min);}return 0;
}

这个方法偏暴力,网上有位大佬利用对称性简化了繁琐过程,请参考:

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;bool test(int k,char s1[],char s2[]){for(int i = 0; s1[k+i] && s2[i]; i++)//这里的循环终止条件很值得学习!if(s1[k+i]+s2[i]-2*'0' > 3)return false;return true;
}int fun(char s1[],char s2[]){int k = 0;while(!test(k,s1,s2))k++;//s1固定,向右移动s2return max(strlen(s1),strlen(s2)+k);
}int main(){char bottom[105],top[105];while(scanf("%s%s",bottom,top) != EOF)//对称性cout<<min(fun(bottom,top),fun(top,bottom))<<endl;return 0;
}

注:原文链接:习题3-11 换低挡装置 UVa1588_Skyline-CSDN博客

习题3-12  浮点数(Floating-Point Numbers,UVa11809)

计算机常用阶码-尾数的方法保存浮点数。如图3-9所示,如果阶码有6位,尾数有8位, 可以

表达的最大浮点数为0.1111111112×211111120.1111111112×21111112。注意小数点后第一位必须为

1,所以一共有9 位小数。

图3-9 阶码-尾数保存浮点数

这个数换算成十进制之后就是0.998046875*2^63=9.205357638345294*10^18。你的任务是

根据这个最大浮点数,求出阶码的位数E和尾数的位数M。输入格式为AeB,表示最大浮点数为

A*10^B。0<A<10,并且恰好包含15位有效数字。输入结束标志为0e0。对于每组数据,输出M和

E。输入保证有唯一解,且0≤M≤9,1≤E≤30。在本题中,M+E+2不必为8的整数倍。

(这道题设计到了作者不太擅长的数据存储知识,题目都没怎么整懂,看了其他作者的分析,略微明白了其思路),在这里选取了一位大佬的题解,请参考:

上面的图就给出了一个例子,前面的0表示是正数。后面8位表示尾数m,这里是0.111111111(注意后面是9个1,因为头一个省略了)。之后那个0表示分割,最后面6位表示e的二进制为111111。所以这个数就是,用十进制表示就是。

  在计算机中用二进制表示M和E的时候如果位数不同,那么它们所能表示的最大值也不同。现在给你所能够表示的最大的浮点数的值,让你倒回去求M和E分别有多少位。输入格式为AeB,表示最大浮点数为,并且0 < A < 10,并且保证输出的结果中0 ≤ M ≤ 9且1 ≤ E ≤ 30。输入以0e0表示结束,0e0本身不计算。

  这个如果直接去算的话相当麻烦,当E很大的时候数会直接超出上限。这个时候可以反过来想,最大的时候M和E的每一位肯定都是1,并且又有0 ≤ M ≤ 9且1 ≤ E ≤ 30的限定,所以一共只有300种情况,自然就想到了打表,先用二重循环枚举M和E可能出现位数的所有情况打一张表,然后输入的时候倒回去找即可。

  假设当前一层M和E的值为m和e,它们的位数分别为i和j。

  首先计算m的值,用二进制表示的话,m的值为0.11…,也就是m = 2^(-1) + 2^(-2) + … + 2^(-1 - i)(i比实际1的个数少1个),也就是m = 1 - 2^(-1 - i)。

  接下来就是计算e的值,不难得出,e = 2^j - 1。

  那么也就有m * 2^e = A * 10^B,似乎可以直接计算了。然而,直接这样算的话是不行的,因为当e太大的话(e最大可以是1073741823,注意这还只是2的指数),等号左边的数就会超出上限,所以要想继续算下去,就得自己去想办法再写出满足要求的类来,这显然太麻烦了。所以,这个时候我们对等式两边同时取对数,这个时候就有 log10(m) + e × log10(2) = log10(A) + B。因为此时m和e的值都是确定的,所以不妨令等式左边为t,也就有t = log10(A) + B。

  这个时候就有问题了,A和B怎么算。

  写题解的时候突然意识到了这个问题,读题的时候很多人,包括我,都把AeB默认为了科学记数法,在ACM协会群里面讨论的时候很多人也都说这是科学计数法。先来看如果是科学记数法的时候应该怎么办。

  如果是科学记数法的话,那么对于A,就有1 ≤ A < 10。那么0 < log10(A) < 1。所以t的小数部分就是log10(A),整数部分就是B,即B = ⌊t⌋,A = 10^(t - B)。那么接下来,我们只需要开出两个二维数组来,分别记录对应i和j下A和B的大小,之后从输入里提取出A和B的大小,去二维数组里面查找对应的i和j即可。

  这种办法在UVA上面是可以直接AC的,但是我却感觉这题这样A了有点数据太水的感觉,秉着处女座+强迫症死磕到底的精神,我们看下哪里有问题。

  其实回头读下题,我们发现科学记数法1 ≤ A < 10的条件是我们脑补出来的,题目里面根本没有提及,只是简单交待0 < A < 10。也就是说,对于确定的M和E的位数,十进制的表示可以有多种,例如样例中的5.699141892149156e76,下面的数据应当也是完全可能的,而且结果应当与样例的结果是相同的(当然是在保证精度可以计算出结果的前提下):

0.569914189214915e77
0.056991418921491e78
0.005699141892149e79
0.000569914189214e80
  带着这个想法我分别拿着上面的数据去UVA toolkit和uDebug上试了试, UVA toolkit依旧能够输出“5 8”的结果来,但是uDebug告诉我我的输入不合法……果真是我想多了么……
  不过这个问题也好办,还是看上面的数据,忽略掉后面几位精度丢失的问题的话,上面的几个数完全可以通过“A *= 10, B -= 1”或者“A /= 10, B += 1”的操作来相互转化。那么对于0 < A < 1的A的值,我们就可以通过“A *= 10, B -= 1”的操作来使其满足科学记数法的条件。

  另外,在查表的时候还应该注意精度的问题,15位有效数字对于double来说精度似乎也不够,而且计算出所需要的整数值其实需要的精度也没有那么高,所以这里的精度就只用到了1e-4的程度。

————————————————
版权声明:本文为CSDN博主「crazysillynerd」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:UVA 11809 - Floating-Point Numbers______-CSDN博客

#include <iostream>
#include <sstream>
#include <string>
#include <cmath>using namespace std;int main() {double M[20][40];long long E[20][40];// 打表for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j) {double m = 1 - pow(2, -1 - i), e = pow(2, j) - 1;double t = log10(m) + e * log10(2);E[i][j] = t, M[i][j] = pow(10, t - E[i][j]);}// 输入并输出结果string in;while(cin >> in && in != "0e0") {// 处理输入for(string::iterator i = in.begin(); i != in.end(); ++i) if(*i == 'e') *i = ' ';istringstream ss(in);double A; int B;ss >> A >> B;while(A < 1) A *= 10, B -= 1;// 在打好的表中寻找答案for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j) {if(B == E[i][j] && (fabs(A - M[i][j]) < 1e-4 || fabs(A / 10 - M[i][j]) < 1e-4)) {cout << i << ' ' << j << endl;break;}}}
}

根据原作者代码转化为C语言版(由于C++没有入门,根据自己的理解和编写习惯,通过浏览相关语句用法来转化为C语言表述):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>int main()
{double M[20][40];long long E[20][40];// 打表for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j){double m = 1 - pow(2, -1 - i), e = pow(2, j) - 1;double t = log10(m) + e * log10(2);E[i][j] = t, M[i][j] = pow(10, t - E[i][j]);}// 输入并输出结果char in[25];while(scanf("%s", in) == 1) {if(strcmp("0e0",in) == 0)break;// 处理输入for(char *i = in; i < in + strlen(in); ++i) if(*i == 'e') *i = ' ';char * p;p = strtok(in, " ");double A = atof(p);p = strtok(NULL,"\0");int B = atoi(p);while(A < 1) A *= 10, B -= 1;// 在打好的表中寻找答案for(int i = 0; i <= 9; ++i) for(int j = 1; j <= 30; ++j) {if(B == E[i][j] && (fabs(A - M[i][j]) < 1e-4 || fabs(A / 10 - M[i][j]) < 1e-4)) {printf("%d %d\n", i, j);break;}}}
}

至此,算法竞赛入门经典(第二版)的所有习题完结,发现大部分题目都涉及到了字符串,拓展了相关方面的知识,但还发现自己还欠缺许多方面的知识,希望能够好好学完这本书,感谢!

算法竞赛入门经典(第二版)第三章习题相关推荐

  1. 补学图论算法:算法竞赛入门经典(第二版)第十一章:

    补学图论算法:算法竞赛入门经典(第二版)第十一章: 倒排索引还没有实现! 下面是左神的图论算法,并查集笔记.和一个美团题目. ''' https://www.nowcoder.com/live/11? ...

  2. UVA-12171 雕塑 题解答案代码 算法竞赛入门经典第二版

    GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 这道题目在<算法竞赛入门经典第二版>书中标注了星号,也是第一道出现星号的 ...

  3. 算法竞赛入门经典第二版课后习题答案第二章

    算法竞赛入门经典第二版课后习题答案 第二章 习题2-1水仙花数 输出100-999中的所有水仙花数.若三位数ABC满足ABC=A^3+B^3+C^3,则称其为水仙花数.例如153=1^3+5^3+3^ ...

  4. 算法竞赛入门经典(第二版)-刘汝佳-第六章 数据结构基础 习题(12/14)

    文章目录 说明 习题 习6-1 UVA 673 平衡的括号 习6-2 UVA 712 S - 树 习6-3 UVA 536 二叉树重建 习6-4 UVA 439 骑士的移动 习6-5 UVA 1600 ...

  5. UVA-814 邮件传输代理的交互 题解答案代码 算法竞赛入门经典第二版

    GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 AC代码 #include<iostream> #include< ...

  6. UVA-1598 交易所 题解答案代码 算法竞赛入门经典第二版

    GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 AC代码 有意思的一个题目.书上说这是一个不错的优先队列练习题,但实际上它其实是一个 ...

  7. 算法竞赛入门经典第二版:循环结构程序设计实例与习题

    实例: 1.阶乘之和 输入n,计算S= 1!+2!+3!+-+n!的末六位. 分析:两个循环,里面循环用于计算不同数的阶乘,外面一个循环用于将所有阶乘相加,核心算法 "for(int i=1 ...

  8. 算法竞赛入门经典(第二版) | 例题4-5 追踪电子表格中的单元格 (UVa512,Spreadsheet Tracking,World Finals)(解法二)

    本着清晰明了易懂可以水两篇 的理念,笔者将这道题分两次发布.这是第二种解法. 第一种解法传送门→解法一+提交网址 因为解法1中有详细关于题目和输入输出格式等的介绍,这里就不过多赘述了. 分析: 一些初 ...

  9. 算法竞赛入门经典(第二版) | 习题3-5 谜题 (UVa227,Puzzle)(World Finals 1993)

    乍一看是一个大水题,但World Finals这两个词标示着老子世界决赛真题虽然题目很水但是数据就能卡死你.整整搞了五个小时,期间经历过崩溃(花了这么多时间搞一道大水题,还没AC),但好在坚持下来了, ...

  10. 算法竞赛入门经典(第二版) | 习题3-10 盒子 (pair结构体)(UVa1587,Box)

    大意: 给定6个矩形的长和宽,判断他们能否构成一个长方体. 题目(提交)链接→UVa-1587 没使用过该网站的同学请猛戳这里→vJudge教程 储备知识: pair结构体: pair是将2个数据组合 ...

最新文章

  1. XHTML5 与 HTML 4.01的差异
  2. mac下安装nginx
  3. C语言 · 矩阵乘法
  4. python列表每行查找字符串,python - 用python查找子字符串列表成字符串列表 - SO中文参考 - www.soinside.com...
  5. FreeRADIUS 测试环境搭建
  6. MySQL千万级别大表如何优化?
  7. 使用VisualSVN Server搭建SVN版本控制服务器
  8. 服务器虚拟网卡驱动卸载,Win10安装和卸载万能网卡版驱动的方法
  9. 传感,驱动,控制-第十章quiz2复习笔记(UTS-41081)
  10. cs架构的软件中服务器作用,cs架构(cs架构基本原理)
  11. 清理win10不常用服务
  12. Flutter自定义背景色渐变 按钮 组件
  13. 第二次作业:《国际贸易学》—自由贸易理论
  14. 在线程中调用PJSIP中的呼叫出现提示注册线程pj_thread_register的解决方法
  15. 荆门市掇刀石中学2021高考成绩查询,2021荆门十大重点高中排名 荆门中学排行榜...
  16. 软件测试和开发哪个好?软件测试就业前景怎样
  17. android版本如何升级包,安卓手机系统怎么升级?安卓手机系统升级教程
  18. 绿盟web应用防火墙(主机版)中国教育和科研计算机网,绿盟远程安全评估系统NSFOCUS RSAS...
  19. BUUCTF Reverse/Ultimate Minesweeper
  20. 【毕业设计】基于stm32的智能电子秤系统 - 物联网 嵌入式 单片机

热门文章

  1. javajavascript:void(‘h2‘)WEB(jsp基本语法表单提交)
  2. 体验华为操作系统 openEuler 20.03 LTS linux
  3. Mysql 新建用户并且授权用户的访问数据库权限以及可以对数据的操作类型
  4. uniapp上下滑屏切换支持视频和图片轮播实现,类似抖音效果
  5. python文献知识图谱可视化_知识图谱可视化工具(知识图谱可视化python)
  6. 毕业后的去向:继续读研还是直接就业?
  7. Python 变量赋值和命名规则
  8. python皮卡丘字符打印代码,python画皮卡丘的代码
  9. [项目管理]工业工程理论在软件项目中的实践
  10. Mysql安装后打开MySQL Command Line Client闪退处理办法,亲测有效