UVA解题集


1 序


系统的整理下第三章的学习笔记。例题代码是在未 看书 本方法前自己尝试并AC的代码,不一定比书上的标程好;习题除了3-8百度了求解方法,其它均独立完成后,会适当查阅网上资料进行整理总结。希望本博文方便自己日后复习的同时,也能给他人带来点有益的帮助(建议配合紫书——《算法竞赛入门经典(第2版)》 阅读 本博客)。有不足或错误之处,欢迎读者指出。 

2 例题

2.1 UVa272--Tex Quotes

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
 
int main() {
     bool logo = 1 ;
     char ch;
 
     while ((ch = getchar()) != EOF) {
         if (ch == '\"' ) {
             if (logo) printf( "``" );
             else printf( "''" );
             logo ^= 1 ;
         }
         else putchar(ch);
     }
 
     return 0 ;
}</stdio.h>
第9~10行:我本来是用puts来输出``和'',发现会自带换行符,所以用printf的%s输出,LRJ的代码是用了这技巧:q?"``":"''"。
第11行:LRJ的标记变量是用q = !q来实现状态变换,我这里用异或位运算效率更高。

2.2 UVa10082--WERTYU

第一次遇这种题,以为是要用if来暴力做的变态题,然后才发现常量数组的妙用。如果不是小白书看过,真的写不出来。 注意转义字符'\'要写成'\\',下面是第一次AC的代码。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
char str[ 4 ][ 50 ] = { "`1234567890-=" , "QWERTYUIOP[]\\" ,
                 "ASDFGHJKL;'" , "ZXCVBNM,./" };
int main() {
     int a[ 200 ], i, j;
 
     for (i = 0 ; i < 4 ; i++)
         for (j = 1 ; a[str[i][j]] = str[i][j- 1 ]; j++);
     a[ ' ' ] = ' ' ; a[ '\t' ] = '\t' ; a[ '\n' ] = '\n' ;
     while ((i = getchar()) != EOF) putchar(a[i]);
     return 0 ;
}
</stdio.h>
第9行:用相当于“哈希映射”的方式,把每个输入字符(str),应该匹配的数值存到一个数组(a)。
第11行:这样用getchar循环读入i,对应输出a[i]就行了。

看了白书后,发现自己的办法有些复杂了(虽然效率上高一点)。为什么要分四个子串呢?完全可以合并成一行,然后用一个循环找,找到后输出前一个字符,找不到就原样输出,避免了原代码中第10行的操作。不过我的方法紫书后续也提到了,下面是我综合下优化后的代码:
?
1
2
3
4
5
6
7
8
9
#include <stdio.h>
char str[ 200 ] = { "`1234567890-=QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,./  \t\t\n\n" };
int main() {
     int a[ 200 ], i;
     for (i = 1 ; a[str[i]] = str[i- 1 ]; i++);
     while ((i = getchar()) != EOF) putchar(a[i]);
     return 0 ;
}
</stdio.h>
第2行:注意末尾的空格、制表符、回车三个字符各连写了两个,这样不用像上次那样多写了第10行。 

2.3 UVa401--Palindromes

这题以前小白书做过,写了很久,不够最后的代码还是挺精炼的。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <string.h>
 
char s[ 100 ], key1[] = "AEHIJLMOSTUVWXYZ12358" , key2[] = "A3HILJMO2TUVMXY51SEZ8" , *p;
 
int ispal()
{
     int i = 0 , j = strlen(s) - 1 ;
     while (i < j) if (s[i++] != s[j--]) return 0 ;
     return 1 ;
}
 
int ismir()
{
     int i = - 1 , j = strlen(s);
     while (++i <= --j)
     {
         p = strchr(key1, s[i]);
         if (p) { if (s[j] != key2[p-key1]) return 0 ;}
         else return 0 ;
     }
     return 1 ;
}
 
int main()
{
     int t;
     while (scanf( "%s" , s) != EOF)
     {
         t = ismir();
         printf( "%s -- is " , s);
         if (ispal()) printf( "%s" , t? "a mirrored palindrome" : "a regular palindrome" );
         else printf( "%s" , t? "a mirrored string" : "not a palindrome" );
         printf( ".\n\n" );
     }
     return 0 ;
 
}</string.h></stdio.h>
关键是如何判断镜像性质:
用两个字符数组存储镜像字符的映射关系。(思想同上题)
没考虑到最中间的字符也要判断是否水平翻转后为其本身,WA几次。(只需略修改循环条件)
学习strchr函数。(该函数找不到字符时返回空指针NULL)  LRJ的代码分析
首先回文串部分LRJ直接在main里判断,我的话没必要为了精简代码而复杂了思维,分成两个子函数不易出错。我的看似短,但总体思路更易于理解。
我是用指针来对应到左右两端,往中间移。LRJ是用下标索引,左是i,右边自然是len-1-i,两种方法都行。
至于他的r函数,正是利用了题目说没有空白输入的特性,写了那个rev字符串,然后将字符直接反转比较。因为字符串肯定没有空格,而反转后非法的字符,在LRJ代码里就变成空格了,肯定匹配不了,所以能巧妙的解决问题! 

2.4 UVa340--Master-Mind Hints

这题小白书也做过,因为数字只有1~9,只要统计每个数字出现的次数,减去强匹配的数量就行。思路同书本但没有那么精简,就不贴代码了。 

2.5 UVa1583--Digit Generator

思路与书本一样。  加载中...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
int y[ 100100 ];
 
int main() {
     for ( int i = 0 ; i < 100000 ; i++) {
         int s = i, t = i;
         while (t) { s+=t% 10 ; t/= 10 ;}
         if (y[s] == 0 ) y[s] = i;
     }
 
     int T, pos;
     scanf( "%d" , &T);
     while (T--) {
         scanf( "%d" , &pos);
         printf( "%d\n" , y[pos]);
     }
     return 0 ;
}</stdio.h>

2.6 UVa1584--Circular Sequence

题目 :输入一个长度在2~100的字符串,要理解成环状,然后能从任意地方作为起点生成一个新的同长度字符串,问这些字符串中字典序最小的是哪个。  分析与求解 :字符串的比较可以使用strcmp函数,将要比较的两个字符串首地址,即指针传入即可。那么,我们可以用一个min_p的值来存储当前字典序最小的字符串起始下标,用i来遍历1~len-1的起始下标,因为是环状,所以每轮i循环,都在字符串y末尾添加对应的字符,并添加'\0'方便strcmp的调用。
方法跟紫书不一样。具体思路和细节,与strcmp的灵活使用,读者可以自己分析思考。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>
 
int main() {
     int T;
     scanf( "%d" , &T);
 
     while (T--) {
         char y[ 300 ];
         scanf( "%s" , y);
 
         int len = strlen(y), i, min_p = 0 ;
         for (i = 0 ; i < len; i++) {
             y[i+len] = y[i];
             y[i+len+ 1 ] = 0 ;
             if (strcmp(y+min_p,y+i+ 1 ) > 0 )
                 min_p = i+ 1 ;
         }
         y[min_p+len] = 0 ;
         printf( "%s\n" , y+min_p);
     }
     return 0 ;
}</string.h></stdio.h>

3 习题

3.1 UVa1585--Score

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
int main() {
     int T;
     scanf( "%d" , &T);
 
     while (T--) {
         char s[ 100 ];
         scanf( "%s" , s);
 
         int sum = 0 , cnt = 0 ;
         for ( int i = 0 ; s[i]; i++) {
             if (s[i]== 'X' ) {
                 sum += cnt*(cnt+ 1 )/ 2 ;
                 cnt = 0 ;
             }
             else cnt++;
         }
         printf( "%d\n" , sum+cnt*(cnt+ 1 )/ 2 );
     }
     return 0 ;
}</stdio.h>
每遇到X做一次更新,否则cnt++,注意第19行最后还要一次更新。

看了这段代码:http://blog.youkuaiyun.com/sinat_17231979/article/details/36869231后,发现还是想复杂了,没必要用前n项和公式,直接用一个变量记录当前有多少个O就行了:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
int main() {
     int T;
     scanf( "%d" , &T);
 
     while (T--) {
         char s[ 100 ];
         scanf( "%s" , s);
 
         int sum = 0 , cnt = 0 ;
         for ( int i = 0 ; s[i]; i++) {
             if (s[i]== 'X' ) cnt = 0 ;
             else {
                 cnt++;
                 sum += cnt;
             }
         }
         printf( "%d\n" , sum);
     }
     return 0 ;
}</stdio.h>

3.2 UVa1586--Molar Mass

暴力每一位i,如果i+1位是数字,判断i+2位是不是数字,分情况计算数量;如果i+1位不是数字,那么数量就是默认的1。接着判断第i位是不是字母,不是则continue,否则累加相应的质量。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <ctype.h>
 
int main() {
     int T;
     scanf( "%d" , &T);
 
     while (T--) {
         char s[ 100 ];
         scanf( "%s" , s);
 
         double sum = 0 , w;
         for ( int i = 0 ; s[i]; i++) {
             // 算出后两位所代表的数值
             if (isdigit(s[i+ 1 ])) {
                 if (isdigit(s[i+ 2 ])) w = 10 *(s[i+ 1 ]- '0' )+s[i+ 2 ]- '0' ;
                 else w = s[i+ 1 ] - '0' ;
             }
             else w = 1 ;
 
             // 判断第一位
             if (!isalpha(s[i])) continue ;
             else if (s[i] == 'C' ) sum += 12.01 *w;
             else if (s[i] == 'H' ) sum += 1.008 *w;
             else if (s[i] == 'O' ) sum += 16.00 *w;
             else sum += 14.01 *w;
         }
         printf( "%.3lf\n" , sum);
     }
     return 0 ;
}</ctype.h></stdio.h>

3.3 UVa1225--Digit Counting

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
int main() {
     int T;
     scanf( "%d" , &T);
 
     while (T--) {
         int N, i, t, a[ 10 ] = {};
         scanf( "%d" , &N);
 
         for (i = 1 ; i <= N; i++) {
             t = i;
             while (t) a[t% 10 ]++, t/= 10 ;
         }
         for (i = 0 ; i < 9 ; i++) printf( "%d " , a[i]);
         printf( "%d\n" , a[ 9 ]);
     }
     return 0 ;
}</stdio.h>
直接每组暴力都能过。如果会TLE,也可以建立二维数组a[10000][10],先暴力一轮求出数据。后面只要打表就行了。 

3.4 UVa455--Periodic Strings

算一个字符串的周期,其周期一定是长度的约数,按这一点枚举,并写一个判断T是不是一个字符串的周期的函数(isThePeriod)即可。

输入格式略坑,每两组测试间会有一个空行,所以我用第19行那样的方式解决。

最后注意输出格式,也要每两个隔一个空行~~~
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <string.h>
 
int isTthePeriod( char s[], int T) {
     for ( int i = 0 ; i < T; i++) {
         for ( int j = i+T; j < strlen(s); j+=T)
             if (s[i] != s[j]) return 0 ;
     }
     return 1 ;
}
 
int main() {
     int T, kase, len, i;
     scanf( "%d" , &T);
 
     for (kase = 1 ; kase <= T; kase++)
     {
         char s[ 100 ];
         while (scanf( "%s" , s), len = strlen(s), !len);
 
         for (i = 1 ; i <= len; i++)
             if (len%i == 0 && isTthePeriod(s,i)) break ;
         if (kase != 1 ) putchar( '\n' );
         printf( "%d\n" , i);
     }
     return 0 ;
}</string.h></stdio.h>

3.5 UVa227--Puzzle

这题稍微有些麻烦了,模拟操作,我尽量进行了些优化,而且为了方便交换变量,用了C++里algorithm的swap,没有自己写交换函数。
【基本框架】
将5*5网格定义为全局变量s(从1开始编号),当前空格所在行列为x,y
output函数用来输出s的矩阵形式
ok用来操作一个命令为ch的移动,返回值1代表操作成功,返回0代表失败。成功的话,要更新相应的x、y和进行交换操作swap
test用来完成每组测试的操作
总结:对于复杂的问题,要想清思路,然后把大问题划分成一个个小的子问题去求解。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cstdio>
#include
using namespace std;
 
int x, y;
char s[ 6 ][ 6 ];
 
void output() {
     for ( int i = 1 ; i <= 5 ; i++) {
         for ( int j = 1 ; j < 5 ; j++)
             printf( "%c " , s[i][j]);
         printf( "%c\n" , s[i][ 5 ]);
     }
}
 
int ok( char ch) {
     switch (ch) {
     case 'A' :
         if (x == 1 ) return 0 ; else x--;
         swap(s[x][y], s[x+ 1 ][y]); break ;
     case 'B' :
         if (x == 5 ) return 0 ; else x++;
         swap(s[x][y], s[x- 1 ][y]); break ;
     case 'L' :
         if (y == 1 ) return 0 ; else y--;
         swap(s[x][y], s[x][y+ 1 ]); break ;
     case 'R' :
         if (y == 5 ) return 0 ; else y++;
         swap(s[x][y], s[x][y- 1 ]); break ;
     }
}
 
int test() {
     char ch;
     int i, j;
     for (i = 1 ; i <= 5 ; i++) for (j = 1 ; j <= 5 ; j++)
         if (s[i][j] == ' ' ) x = i, y = j;
 
     while ((ch = getchar()) != '0' ) {
         if (ch == '\n' ) continue ;
         if (!ok(ch)) {  // 非法操作,仍要清掉字符'0'前的值
             while (getchar() != '0' );
             return 0 ;
         }
     }
 
     return 1 ;
}
 
int main() {
     int kase, i, j;
 
     for (kase = 1 ; ; kase++) {
         gets(&s[ 1 ][ 1 ]);
         if (s[ 1 ][ 1 ] == 0 ) {kase--; continue ;}   // 注意此处读到空串,要忽略读入continue
         if (s[ 1 ][ 1 ] == 'Z' && s[ 1 ][ 2 ] == 0 ) break ;
 
         if (kase != 1 ) putchar( '\n' );
         printf( "Puzzle #%d:\n" , kase);
         for (i = 2 ; i <= 5 ; i++) gets(&s[i][ 1 ]);
         if (test()) output();
         else printf( "This puzzle has no final configuration.\n" );
     }
     return 0 ;
}</algorithm></cstdio>

3.6 UVa232--Crossword Answers

这题就是像中学英语填单词的那种网格。需要对起始点编号,然后按行输出、再按列输出所有单词。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <string.h>
int main() {
     int n, m, a[ 15 ][ 15 ], k, i, j;
     char s[ 15 ][ 15 ];
 
     for ( int kase = 1 ; ; kase++) {
         if (scanf( "%d%d " , &n, &m) != 2 ) break ;
 
         k = 0 ;
         memset(a, 0 , sizeof(a));
         memset(s, 0 , sizeof(s));
 
         for (i = 1 ; i <= n; i++) gets(&s[i][ 1 ]);
         for (i = 1 ; i <= n; i++) for (j = 1 ; j <= m; j++) {
             if (s[i][j] == '*' ) s[i][j] = 0 ;
             if (!(s[i- 1 ][j]&&s[i][j- 1 ]) && s[i][j]) a[i][j] = ++k;
         }
 
         if (kase != 1 ) putchar( '\n' );
         printf( "puzzle #%d:\nAcross\n" , kase);
         for (i = 1 ; i <= n; i++) for (j = 1 ; j <= m; j++)
             if (s[i][j] && !s[i][j- 1 ]) printf( "%3d.%s\n" , a[i][j], s[i]+j);
         printf( "Down\n" );
         for (i = 1 ; i <= n; i++) for (j = 1 ; j <= m; j++) {
             if (s[i][j] && !s[i- 1 ][j]) {
                 printf( "%3d." , a[i][j]);
                 for (k = i; s[k][j]; k++) putchar(s[k][j]);
                 putchar( '\n' );
             }
         }
     }
     return 0 ;
}</string.h></stdio.h>

3.7 UVa1368--DNA Consensus

思路:找出每列中ACGT出现次数最多的字符,就是最优解序列在这一列的字符值。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <string.h>
 
inline int c2d( char c) {
     if (c == 'A' ) return 0 ;
     else if (c == 'C' ) return 1 ;
     else if (c == 'G' ) return 2 ;
     else return 3 ;
}
 
inline int d2c( int d) {
     if (d == 0 ) return 'A' ;
     else if (d == 1 ) return 'C' ;
     else if (d == 2 ) return 'G' ;
     else return 'T' ;
}
 
int main() {
     int T, m, n, i, j, a[ 1010 ][ 4 ], sum;
     char s[ 50 ][ 1010 ];
     scanf( "%d" , &T);
 
     while (scanf( "%d%d " , &m, &n) == 2 ) {
         for (i = 0 ; i < m; i++) gets(s[i]);
         memset(a, 0 , sizeof(a));
         for (i = 0 ; i < m; i++) for (j = 0 ; j < n; j++)
             a[j][ c2d(s[i][j]) ]++;
         for (sum = j = 0 ; j < n; j++) {
             int the_max = a[j][ 0 ], id = 0 ;
             for (i = 1 ; i < 4 ; i++) if (a[j][i]>the_max)
                 id = i, the_max = a[j][i];
             putchar(d2c(id));
             sum += m - the_max;
         }
         printf( "\n%d\n" , sum);
     }
     return 0 ;
 
}</string.h></stdio.h>

3.8 UVa202--Repeating Decimals

这题需要模拟除法,当出现相同的余数时,就代表循环点的出现。下面我以5/7为例详细的讲下思路。  加载中...
如上图,第0步,分子是5,分母是7(分母在整个除法中始终不变),5/7的商是0,代表这个除法的整数部分是0,,余数是5,乘以10,得50作为第1步的分子;50/7的商为7,代表这个除法的第一位小数是7,余数是1,乘以10,得10作为第2步的分子...可以发现每步的除法只跟分子有关,也就是当分子出现重复值,如上图第1步和第7步的分子值都为50时,第7步后面的运算其实是在重复第1步至第6步间的运算。所以第1步至第7步就是一个循环节。 程序实现中,可以不用存储余数的值,只需存储上图中的分子和商,我定义了A[2][3010],用A[0][j]存储第j步的分子,A[1][j]存储第j步的商。注意最后的数值由商可以直接构成,如上图的红色字体,数值为0.7142857142...
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
 
int A[ 2 ][ 3010 ], p, q;
 
// 找i位之前是否有重复分子出现;有则返回周期,否则返回-1.
inline int myfind() {
     for (p = 1 ; p < q; p++) {
         if (A[ 0 ][p] == A[ 0 ][q])
             return q - p;
     }
     return - 1 ;
}
 
int main() {
     int kase, a, b, i, len;
     for (kase = 1 ; scanf( "%d%d" , &a, &b) != EOF; kase++) {
         A[ 0 ][ 0 ] = a; A[ 1 ][ 0 ] = a/b;
         for (q = 1 ; ; q++) {
             A[ 0 ][q] = (A[ 0 ][q- 1 ] - b*A[ 1 ][q- 1 ])* 10 ;
             A[ 1 ][q] = A[ 0 ][q]/b;
             len = myfind();
             if (len > 0 ) break ;
         }
         printf( "%d/%d = %d." , a, b, A[ 1 ][ 0 ]);
         for (i = 1 ; i < p; i++) printf( "%d" , A[ 1 ][i]);
         putchar( '(' );
         for (i = p; i < q; i++) {
             if (i > 50 ) {
                 printf( "..." );
                 break ;
             }
             printf( "%d" , A[ 1 ][i]);
         }
         printf( ")\n   %d = number of digits in repeating cycle\n\n" , len);
     }
 
}</stdio.h>

3.9 UVa10340--All in All

遍历后者,找前者往后是否有对应的字符。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string>
#include <iostream>
using namespace std;
 
int fun(string& a, string& b) {
     int i, j = 0 ;
     for (i = 0 ; i < a.size(); i++, j++) {
         while (j < b.size() && a[i]!=b[j]) j++;
         if (j == b.size()) return 0 ;
     }
     return 1 ;
}
 
int main() {
     string a, b;
     while (cin >> a >> b) {
         if (fun(a,b)) cout << "Yes\n" ;
         else cout << "No\n" ;
     }
}</iostream></string>

3.10 UVa1587--Box

只要用一堆疯狂的条件分支就能解答此题了:http://blog.youkuaiyun.com/u013451221/article/details/37672039。不过这里我更乐意把数据“标准化”,然后用C++构造“Face类”来求解。 首先构造一个宽w一定不小于高h的“面类(Face)”,并且按h为第一级,w为第二级准则从小到大排序。如果是能组成长方体的,边长肯定只有三种值,不妨设从小到大依次为a,b,c。则输入的6个面排序后,一定满足以下分布:
加载中... vczltcSz5NKqzPW8/qGjCjxwcmUgY2xhc3M9"brush:java;">#include #include #include using namespace std; struct Face{ int h, w; void make(int x, int y) { h = min(x,y); w = max(x,y); } bool operator < (const Face& b) const { if (h == b.h) return w < b.w; else return h < b.h; } bool operator == (const Face& b) const { return (h == b.h) && (w == b.w); } }; vector A(6); int test1() { for (int i = 0; i < 6; i += 2) if (!(A[i]==A[i+1])) return 0; return 1; } int test2() { if (A[0].h==A[2].h && A[0].w==A[4].h && A[2].w==A[4].w) return 1; else return 0; } int main() { int x, y; while (cin >> x >> y) { A[0].make(x, y); for (int i = 1; i < 6; i++) { cin >> x >> y; A[i].make(x, y); } sort(A.begin(), A.end()); if (test1() && test2()) cout << "POSSIBLE\n"; else cout << "IMPOSSIBLE\n"; } return 0; }

3.11 UVa1588--Kickdown

假设固定装置s1,那么装置s2只要初始状态左端与s1左端对齐(该状态用偏移量k=0来标记),然后测试k=0时,两个装置是不是”每一位的和都不会超过3“就行了,如果不行就k++,继续往右边偏移进行测试。可以知道肯定存在着一个k能使其成立的(也就是s2左端接在s1右端时)。用这种方式遍历,找到的第一个可行解就是最佳解。 不过上述讨论还遗漏了一种组合方式,就是s2也可以往左偏移。其实将上述解法写成一个fun函数进行封装,外界(main函数)只要巧妙的利用对称性,就能重复利用上一段的代码了。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <string.h>
#include
using namespace std;
 
int len1, len2;
char s1[ 110 ], s2[ 110 ];
 
int 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 0 ;
     return 1 ;
}
 
int fun( char s1[], char s2[]) {
     int k = 0 ;
     while (!test(k,s1,s2)) k++;
     return max(strlen(s1), strlen(s2)+k);
}
 
int main() {
     while (scanf( "%s%s" , s1, s2) != EOF) {
         printf( "%d\n" , min(fun(s1,s2), fun(s2,s1)));
     }
     return 0 ;
}</algorithm></string.h></stdio.h>

3.12 UVa11809--Floating-Point Numbers

首先要知道二进制小数的原理:  加载中...
本题的思路就是先把所有的M、E对应的值算出来,记为矩阵C,其中C[M,E]=尾数M、阶码E对应的最大值。然后对于输入的AeB,找出C中与其相等的值所在的行M、列E即是答案。 不过这里有两个要点。一是这个数值太大,可以达到2^(2^30-1),远超了double的存储范围。所以我们不妨对数值做一个变换,取以10为底的对数值来存储。二是计算过程的舍尾误差与double本身存在的误差会导致精度问题。所以可以在矩阵C中找与输入值最接近的作为匹配点,也就是差值的绝对值最小。
最后还有M=0,E=1的特殊点,在程序中会出现0.0-0.0<0.0条件为否的错误,导致pi,pj无法更新,这个问题只要把pi、pi分别初始化为0、1就能解决了。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <math.h>
#include <stdio.h>
#include <string.h>
 
double C[ 10 ][ 31 ];
 
void init() {
     int i, j, v;
     double f[ 10 ] = { 0.5 }, g[ 31 ], t;
 
     for (i = 1 , t = 0.5 ; i < 10 ; i++) {
         f[i] = f[i- 1 ] + t/ 2 ;
         t /= 2 ;
     }
     for (i = 0 ; i < 10 ; i++) f[i] = log10(f[i]);
     for (i = 1 , v = 2 ; i <= 30 ; i++) {
         g[i] = (v - 1.0 )*log10( 2.0 );
         v <<= 1 ;
     }
     for (i = 0 ; i < 10 ; i++) for (j = 1 ; j <= 30 ; j++)
         C[i][j] = f[i] + g[j];
}
 
int main() {
     int i, j;
     char s[ 100 ], *p;
     double A, B;
 
     init();
 
     while (scanf( "%s" , s), strcmp(s, "0e0" )) {
         p = strchr(s, 'e' );
         *p = 0 ;                 // 将'e'所在位置变为'\0'
         sscanf(s, "%lf" , &A);
         sscanf(p+ 1 , "%lf" , &B);
 
         int pi = 0 , pj = 1 ;
         B = A = log10(A) + B;   // 接下来A存储表达式值,B记录差值
         for (i = 0 ; i < 10 ; i++) for (j = 1 ; j <= 30 ; j++)
         if (fabs(A-C[i][j]) < B) pi = i, pj = j, B = fabs(A-C[i][j]);
         printf( "%d %d\n" , pi, pj);
     }
     return 0 ;
}</string.h></stdio.h></math.h>
转载网址:http://www.2cto.com/kf/201408/322120.html  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值