算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题

文章目录

    • 习题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)
        • 题目描述
        • 问题分析
        • 代码
    • 【补充】剑指 Offer 20. 表示数值的字符串
        • 题目描述
        • 问题解析
        • 代码
    • 习题3-5 谜题(Puzzle, ACM/ICPC World Finals 1993, UVa227)
        • 题目描述
        • 问题分析
        • 代码
        • 【知识点】C/C++ 中指针和引用的区别?
    • 习题3-6 纵横字谜的答案(Crossword Answers, ACM/ICPC World Finals 1994,UVa232)
        • 题目描述
        • 问题分析
        • 代码
        • 【知识点】setw(int n)函数——C++中输出宽度设置
    • 习题3-7 DNA序列(DNA Consensus String, ACM/ICPC Seoul 2006, UVa1368)
        • 题目描述
        • 问题分析
        • 代码
        • 【知识点】C++ 中max()与max_element()的区别
    • 习题3-8 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)
        • 题目描述
        • 问题分析
        • 代码
        • 【知识点】push_back()方法(vector)
    • 习题3-9 子序列(All in All, UVa 10340)
        • 题目描述
        • 问题分析
        • 代码
    • 习题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。

样例输入
5
OOXXOXXOOO
OOXXOOXXOO
OXOXOXOXOXOXOX
OOOOOOOOOO
OOOOXOOOOXOOOOX

样例输出
10
9
7
55
30

问题分析

直接判断OX串,若为O,则累加器++;当累加器遇到X时,重置为零。

代码

#include 
#include 

using namespace std;

int main(){
	int T;
	cin >> T;
	
	while(T--){
		char str[85];
		int sum = 0, count = 0;
		cin >> str;
		int len = strlen(str);
		
		for(int i=0; i<len; i++){
			if(str[i] == 'O'){
				count ++;
				sum += count;
			}
			else count = 0;
		}
		cout << sum << endl;
	}
	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。

样例输入
4
C
C6H5OH
NH2CH2COOH
C12H22O11

样例输入
12.010
94.108
75.070
342.296

问题分析

分子式中的分子有两种情况:
1、原子后无数字,直接判断是什么原子,加入原子量
2、原子后有数字,判断数字为多少、原子是什么,计算此原子总原子量

代码

#include 
#include 
#include 
#include 

using namespace std;

int mol(char* s, int l, int k){
	int t = 0;
	while(k<l){
		if(isdigit(s[k+1])){
			t = t*10 + (s[k+1]-'0');
			k++;
		}
		else break;
	}
	return t;
}
 
int main(){
	int T;
	cin >> T;
	
	while(T--){
		double sum = 0.0;
		char s[105];
		cin >> s;
		int len = strlen(s);
		
		for(int i=0; i<len; i++){
			if(isalpha(s[i]) && isdigit(s[i+1])){
				int t = mol(s, len, i);
				switch(s[i]) {
					case 'C':sum += 12.01*t; break;
					case 'H':sum += 1.008*t; break;
					case 'O':sum += 16.00*t; break;
					case 'N':sum += 14.01*t; break;
				}
			}
			else{
				switch(s[i]) {
					case 'C':sum += 12.01; break;
					case 'H':sum += 1.008; break;
					case 'O':sum += 16.00; break;
					case 'N':sum += 14.01; break;
				}
			}
			
		}
		printf("%.3f",sum);
	}
	
	return 0;
}

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

题目描述

把前n(n≤10000)个整数顺次写在一起:123456789101112…数一数0~9各出现多少次(输出10个整数,分别是0,1,…,9出现的次数)。
样例输入
2
3
13
样例输出
0 1 1 1 0 0 0 0 0 0
1 6 2 2 1 1 1 1 1 1

问题分析

从1遍历到n,用一个数组a存放0~9各出现的数字

代码

#include 
#include 

using namespace std;

int main(){
	int T, n;
	int a[11];
	
	cin >> T;
	memset(a, 0, sizeof(a));
	
	while(T--){
		cin >> n;
		for(int i=1; i<=n; i++){
			int t=i;
			while(t){
				a[t%10]++;
				t /= 10;
			}
		}
		
		for(int i=0; i<10; i++){
			cout << a[i] << " ";
		}
		cout << endl;
	}
	return 0;
}

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

题目描述

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

样例输入
1
HoHoHo

样例输出
2

问题分析

要求出最小周期,首先遍历字符串,当遍历到的字符与首位字符s[0]不相同则周期n++,当与s[0]相同,则开始验证n是否为最小周期。
首先,若n确实是周期,则一定能被字符串长度整除,若不能整除,则肯定不是周期,那么则继续遍历;
但是若n能被字符串长度整除,也不能确定就一定是周期,要进一步判断,在这里设一个循环,循环次数为(字符串长度/n)-1,这个个数就是后面的字符串重复第一个周期的次数,每次都进入函数判断,是否子串与首个周期相同,若每次都相同。则找到了最小周期。

代码

#include 
#include 

using namespace std;

//判断子串与首个周期是否相同
int f(int res, int j, char* s){
	int i, a = 0;
	for(i = j*res+0; i<j*res+res; i++){
		if(s[i] != s[a]) 
			return 0;
		a++;
	}
	return 1;
}

int main(){
	int T;
	cin >> T;
	
	while(T--){
		int res = 1, flag = 0;
		char s[85];
		cin >> s;
		int len = strlen(s);
		
		for(int i=1; i<len; i++){
			if(s[i] != s[0]) res++;
			
			else{
				if(len % res != 0){
					res++;
					continue;
				}
				else{
					int x = len/res;//x为最小周期个数
					for(int j=1; j<x; j++){
						if(f(res, j, s))
							flag = 1;
						else{
							res++;
							flag = 0;
							break;
						}
					}
					if(flag) break;//找出最小周期res,退出循环;
				}
			}
		}
		
		cout << res << endl;
		if(T) cout << endl;
	} 
	return 0;
}

【补充】剑指 Offer 20. 表示数值的字符串

来源:力扣(LeetCode)

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。

问题解析

主要是判断一个表示数值的字符串是否合法,判断主要包括:整数部分,小数部分,指数部分,符号部分。 我们做题的思想就是判断上述各部分是否合法,如果有一部分不合法,那么整体就是不合法的。

其实本题的难点就是如何去对整数,小数,指数,符号判断合法?
符号部分:
第一次符号出现的位置可能是开头,也可能是在指数部分e/E之后(注意必须是紧挨着e/E)。
第二次符号出现的位置只能是指数部分,紧挨着(e/E)。
整数部分:
字符是否在0-9之间。
小数部分:
不能含有多个小数点,并且不能出现在指数e/E的后边。
指数部分:
不能有多个e/E指数部分,指数e/E符号后边一定要是整数。

代码

class Solution {
public:
    bool isNumeric(char* str)
    {
        // 标记符号、小数点、e是否出现过
        bool sign = false, decimal = false, hasE = false;
        for (int i = 0; i < strlen(str); i++) {
            // 先判断正负号:第一次出现的话不是在开头就是在指数e/E紧接着。第二次出现一定紧接着e/E
            if(str[i] == '+' || str[i] == '-')
            {
                if(!sign && i > 0 && str[i-1] != 'e' && str[i-1] != 'E') // 第一次符号
                    return false;
                if(sign && str[i-1] != 'e' && str[i-1] != 'E') // 第二次符号
                    return false;
                sign = true;
            }
            else if (str[i] < '0' || str[i] > '9') // 判断字符是不是合法,如'a'通过ascii码的数值
                return false;
            else if(str[i] == '.') // 判断小数点:不能重复出现,不能出现在e/E后面
            {
                if(hasE || decimal)
                    return false;
                decimal = true;
            }
            else if(str[i] == 'e' || str[i] == 'E') // 判断指数:后边不能没有整数,不能有两个e
            {
                if(i == (strlen(str) - 1)) // 指数在最后一位不合法
                    return false;
                if(hasE) // 指数又接指数不合法
                    return false;
                hasE = true;
            }
        }
        return true;
    }
};

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

题目描述

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

算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第1张图片

问题分析

看似连续变化,实质是点与点的交换形成的连续,利用数组形成初始网格,根据输入的指令在数组中进行字母与空格的交换,若指令执行后下标越界,则指令非法。

代码

#include 

using namespace std;

char s[5][5] = {'T', 'R', 'G', 'S', 'J',
				'X', 'D', 'O', 'K', 'I',
				'M', ' ', 'V', 'L', 'N',
				'W', 'P', 'A', 'B', 'E',
				'U', 'Q', 'H', 'C', 'F'};
char order[100];
int x = 2, y = 1, k = 0;

void set_xy(){
	if(order[k] == 'A') x--;
	if(order[k] == 'B') x++;
	if(order[k] == 'L') y--;
	if(order[k] == 'R') y++;
}

int main(){
	cin >> order;
	int i, j;
	
	while(order[k] != '\n' && order[k] != '0'){
		i = x;
		j = y; 
		
		set_xy();
		if(x<0 || x>=5 || y<0 || y>=5){
			cout << "This puzzle has no final configuration." << endl;
			return 0;
		}
		
		s[i][j] = s[x][y];
		s[x][y] = ' ';
		
		k++;
	}
	
	for(int i=0; i<5; i++){
		int flag = 0;
		
		for(int j=0; j<5; j++){
			if(flag) cout << ' '; //输出一行后不能有空格 
			flag = 1;
			cout << s[i][j];
		}
		cout << endl;
	}
	
	return 0;
} 

【知识点】C/C++ 中指针和引用的区别?

  1. 指针有自己的一块空间,而引用只是一个别名;
  2. 使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;
  3. 指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用;
  4. 作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
  5. 可以有const指针,但是没有const引用;
  6. 指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被改变;
  7. 指针可以有多级指针(**p),而引用只有一级;
  8. 指针和引用使用++运算符的意义不一样;
  9. 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。

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

题目描述

输入一个r行c列(1≤r,c≤10)的网格,黑格用“ * ”表示,每个白格都填有一个字母。如果一个白格的左边相邻位置或者上边相邻位置没有白格(可能是黑格,也可能出了网格边界),则称这个白格是一个起始格。首先把所有起始格按照从上到下、从左到右的顺序编号为1, 2, 3,…,如图3-7所示。

算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第2张图片

图3-7 r 行 c 列网格

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

原题 Crossword Answers UVA - 232

样例输入

2 2
AT
*O
6 7
AIM*DEN
*ME*ONE
UPON*TO
SO*ERIN
*SA*OR*
IES*DEA
0

样例输出
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第3张图片

问题分析

1、两个数组,一个表示网格,另一个标记起始格;
2、起始格的条件:白格的左边相邻位置或者上边相邻位置是黑格或网格边界;
3、遍历输出

代码

#include 
#include 
#include 
#include 

using namespace std;

int main(){
	int r, c, cas = 0;
	char a[11][11];
	int b[11][11];
	
	memset(b, 0, sizeof(b));
	
	while(cin >> r, r){
		int c, k = 0;
		cin >> c;
		
		memset(b, 0, sizeof(b));
		
		//输入 
		for(int i=0; i<r; i++){
			for(int j=0; j<c; j++){
				cin >> a[i][j];
			}
		}
		
		//标记起始格 
		for(int i=0; i<r; i++){
			for(int j=0; j<c; j++){
			if(isalpha(a[i][j]) && (i-1<0 || j-1<0))
				b[i][j] = ++k;
			else if(isalpha(a[i][j]) && (a[i-1][j]=='*' || a[i][j-1]=='*'))
				b[i][j] = ++k;
			}
		}
		
		if(cas != 0) cout << endl;
		cout << "puzzle #" << ++cas << ":" << endl;
		
		//输出横向 
		cout << "Across" << endl;
		for(int i=0; i<r; i++){
			int j = 0;
			while(j<c){
				if(a[i][j] == '*'){
					j++;
					continue;
				}
				if(j<c)
					cout << setw(3) << b[i][j] << '.';
				
				while(j<c && a[i][j]!='*'){
					cout << a[i][j];
					j++;
				}
				cout << endl;
			}
		}
		
		//输出竖向 
		cout << "Down" << endl;
		for(int i=0; i<r; i++){
			for(int j=0; j<c; j++ ){
				if(a[i][j] == '*' || b[i][j]==0) continue;
				cout << setw(3) << b[i][j] << '.';
				
				int m = i;
				while(m<r && a[m][j]!='*'){
					cout << a[m][j];
					m++;
				}
				cout << endl;
			}
		}
	}
	
	return 0;
} 

【知识点】setw(int n)函数——C++中输出宽度设置

1、setw(int n) 函数用于设置要在输出操作上使用的字段宽度,头文件为 #include
例如:

cout<<'s'<<setw(8)<<'a'<<endl;

则在屏幕显示
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第4张图片

sa之间有 7 个空格,setw()只对其后面紧跟的输出产生作用,如上例中,表示a共占 8 个位置,不足的用空格填充。若输入的内容超过setw()设置的长度,则按实际长度输出。

2、setw()默认填充的内容为空格,可以setfill()配合使用设置其他字符填充。
如:

cout <<'s' << setfill('*') << setw(8) << 'a' << endl;

则输出:
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第5张图片
7个*和字符a共占8个位置。所谓域宽,就是输出的内容(数值或字符等等)需要占据多少个字符的位置,如果位置有空余则会自动补足。比如我们要设置域宽为2,那么当输出一位数1的时候输出的就是“ 1”,即在1前面加了一个空格。空格和数字1正好一共占用了两个字符的位置。

3、setw()默认的是输出右对齐,而当你想要左对齐时,便需要另外添加setiosflags(ios::left)来实现输出左对齐;

cout  << 's' << setw(8) << setiosflags(ios::left) << setfill('*') << 'a' << endl;

算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第6张图片

习题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

样例输入
3
5 8
TATGATAC
TAAGCTAC
AAAGATCC
TGAGATAC
TAAGATGT
4 10
ACGTACGTAC
CCGTACGTAG
GCGTACGTAT
TCGTACGTAA
6 10
ATGTTACCAT
AAGTTACGAT
AACAAAGCAA
AAGTTACCTT
AAGTTACCAA
TACTTACCAA

样例输出
TAAGATAC
7
ACGTACGTAA
6
AAGTTACCAA
12

问题分析

题中引入了Hamming距离的定义,看似要与m个序列比较求最小和十分麻烦,实际上是求每个序列的对应位置出现次数的最多的字母,然后输出,即分别找出每一列哪个元素重复次数最多。
对于最小总Hamming距离,即DNA序列的个数m减去求出的最大出现次数。

代码

#include 
#include 
#include 
#include 
using namespace std;

int main(){
	int t;
	scanf("%d", &t);
	
	while(t--){
		int m, n, i, j;
		scanf("%d %d", &m, &n); 
		
		//输入DNA序列 
		char DNA[50][1010];
		for(i=0; i<m; i++) 	
			scanf("%s", DNA[i]);
		
		char rDNA[1010];  //最优DNA序列 
		int res = 0, ACGT[4] = {0, 0, 0, 0}; //res:最小总的Hamming距离和
		
		// ACGT[4]:统计每列 'A''C''G''T'的数量
		for(i=0; i<n; i++){
			for(int j=0; j<m; j++){
				switch(DNA[j][i]){
					case 'A':
						ACGT[0]++; break;
					case 'C':
						ACGT[1]++; break;
					case 'G':
						ACGT[2]++; break;
					case 'T':
						ACGT[3]++; break;
				}
			}
			
			//找出每一列元素重复次数最多的字母,放在rDNA[i]中
			for(int k=0; k<4; k++){
				if(ACGT[k] == *max_element(ACGT, ACGT+4)){
					switch(k){
						case 0:
							rDNA[i] = 'A'; break;
						case 1:
							rDNA[i] = 'C'; break;
						case 2:
							rDNA[i] = 'G'; break;
						case 3:
							rDNA[i] = 'T'; break;
					}
					res += m - ACGT[k]; // 最小总的Hamming距离和 
					break;
				}
			}
			memset(ACGT, 0, sizeof(ACGT)); //换列应将ACGT[4]置零 
		}
		
		rDNA[n] = 0;  //最优DNA序列加上结束标识符 
		printf("%s\n%d\n", rDNA, res);
	}
	return 0;
}

【知识点】C++ 中max()与max_element()的区别

两者头文件都是#include
max(a,b)返回a,b两者之间的较大值
max_element(r, r+6)返回数组r中[0, 6)之间的最大值的迭代器;
使用max_element返回的值减去数组头地址即为该最大值在数组的序号
minmin_element的区别同上,看到下面的例子你就会明白了

程序示例

#include 
#include 
using namespace std;

int main(){
	int a[6] = {5, 3, 2, 6, 1, 4};
	int b = a[0];
	int c = a[1];
	
	cout << max(b, c) << " " << min(b,c) << endl; //输出为5 3
	
	cout << max_element(a, a+6) - a << endl; // 输出为3 ,即该最大值在数组的序号
	cout << *max_element(a, a+6) << endl; //输出为 6 ,即该数组最大值
	
	cout << min_element(a, a+6) - a << endl; // 输出为 4 ,即该最小值在数组的序号
	cout << *min_element(a, a+6) << endl;	 //输出为 1 ,即该数组最小值
	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。

输入
输入文件的每一行由一个非负的整数分子和一个正的整数分母组成。没有一个输入整数超过3000。文件结束表示输入结束。

输出
对于每一行输入,打印分数,其小数扩展通过循环的第一次出现到小数点右边或50位小数(以先出现的为准),以及整个重复循环的长度。在编写十进制扩展时,尽可能用括号将重复循环括起来。如果整个重复循环没有在前50个位置出现,在循环开始的地方放一个左括号——它将在前50个位置开始——并放置`…)”第50位数字后。在每个测试用例后打印一个空行。

样例输出
76 25
5 43
1 397

样例输出
76/25 = 3.04(0)
1 = number of digits in repeating cycle

5/43 = 0.(116279069767441860465)
21 = number of digits in repeating cycle

1/397 = 0.(00251889168765743073047858942065491183879093198992…)
99 = number of digits in repeating cycle

问题分析

整数部分:a/b;
小数部分:循环节,模拟除法,以 384/1111 = 0.(3456) 为例:
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第7张图片
用一个数组存放每一步的被除数,一个数组存放小数部分;

代码

#include 
#include 
using namespace std;

int main(){
	int a, b;
	
	while(cin >> a >> b){
		vector <int> dividend; //记录每一步的被除数 ,如 3840,5070,6260,7050 ...
		vector <int> decimal; //记录小数部分,如 3456.... 
		
		int aa = a % b; //暂存 被除数 
		int cycle_begin = 0; //循环节的开始 
		
		while(1){
			aa = aa%b * 10; 
			bool is_over = false;
			
			//判断 循环节 
			int div_len = dividend.size();
			for(int i=0; i < div_len; i++){
				if(dividend[i] == aa){
					cycle_begin = i;
					is_over = true;
				}
			}
			if(is_over) break;
			
			decimal.push_back(aa/b); //记录每一步的对应小数 
			dividend.push_back(aa); //记录每一步的被除数
		}
		
		int cycle_length = dividend.size(); //循环节长度 
		
		
		int count_out = 0;  
		cout << a << "/" << b << " = ";
		cout << a / b << ".";
		
		//输出非循环小数部分 
		for(int i = 0; i < cycle_begin; i++){
			cout << decimal[i];
			count_out ++;
		}
		
		//输出循环节部分 
		cout << "(";
		for(int i = cycle_begin; i < decimal.size(); i++){
			cout << decimal[i];
			count_out ++;
			if(count_out == 50){ //控制所输出的小数长度在50以内
				cout << "...";
				break;
			}
		}
		cout << ")";
		cout << endl;
		
		cout << "   " << cycle_length << " = " << "number of digits in repeating cycle" << endl;
		cout << endl;
		
	}
	
	return 0;
} 

【知识点】push_back()方法(vector)

vector::void push_back (const value_type& val);
vector::void push_back (value_type&& val);

该函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素,新的元素的值是val的拷贝(或者是移动拷贝)

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

题目描述

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

输入规范
输入包含几个测试用例。每一个都由两个由空格分隔的字母数字ascii字符组成的字符串s,t来指定。eof终止输入。

输出规范
如果s是t的子序列,则每个测试用例输出

样例输入
sequence subsequence

person compression

VERDI vivaVittorioEmanueleReDiItalia

caseDoesMatter CaseDoesMatter

样例输出
Yes

No

Yes

No

问题分析

遍历比较字符串st,若s的第k(k=0,1 … slen)个字符与t的第i(i=k,k+1… tlen)个字符形同则比较st的下一个字符,否则,比较sk个字符和ti+1个字符;
由题意知,slen <= tlen,若在t字符串遍历之前结束s,则st的子序列,输出Yes,若字符串t遍历结束时,s还没有结束遍历,则s不是t的子序列No

代码

#include 
#include 
#include 
using namespace std;

int main(){
	char s[10005], t[10005];
	while(scanf("%s %s", s, t) != EOF){
		int tlen = strlen(t);
		int slen = strlen(s);
		
		bool flag = false;
		int k = 0, i = 0;
		
		for(; i<tlen; i++){
			if(s[k] == t[i]){
				k++;
			}
			if(k == slen){
				flag = true;
				break;
			}
		}
		
		if(flag) printf("Yes\n");
		else printf("No\n");
	} 
	return 0;
} 

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

题目描述

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

输入
输入文件包含几个测试用例。每一个都由六行组成。每行描述一个托盘,包含两个整数 w 和 h ( 1 ≤ w,h ≤10 000) 托盘的宽度和高度,单位分别为毫米。
输出
对于每个测试用例,打印一行输出。如果有可能用六个给定的托盘做一个盒子,在输出文件中写一个单词“可能”。如果不可能写一个单词“不可能”。

样例输出
1345 2584
2584 683
2584 1345
683 1345
683 1345
2584 683
1234 4567
1234 4567
4567 4321
4322 4567
4321 1234
4321 1234

样例输出
POSSIBLE
IMPOSSIBLE

问题分析

我们知道,一个盒子六个面,前后,左右,上下的 ‘面’ 两两相同即有三组 ‘面’ 是相同的;所以我们将每组wh相乘得到六个面,只需判断这六个面是否有三组相同的面,若有,则说明有可能成为一个盒子,否则不可能。

代码

#include 
#include 
#include 
using namespace std;

int main(){
	int w, h;
	
	while(scanf("%d %d", &w, &h) != EOF){
		int wh[7];
		wh[0] = w*h;
		for(int i=1; i<6; i++){
			cin >> w >> h;
			wh[i] = w*h;
		}
		
		int count = 0;
		for(int i=0; i<6; i++){
			for(int j=i+1; j<6; j++)
				if(wh[i] == wh[j])
					count++;
		}
		
		if(count == 3) printf("POSSIBLE\n");
		else printf("IMPOSSIBLE\n");
	}
	return 0;
} 

——————------------- 待续…

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

给出两个长度分别为n1,n2(n1,n2≤100)且每列高度只为1或2的长条。需要将它们放入一个高度为3的容器(如图3-8所示),问能够容纳它们的最短容器长度。
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第8张图片

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

计算机常用阶码-尾数的方法保存浮点数。如图3-9所示,如果阶码有6位,尾数有8位,可以表达的最大浮点数为0.1111111112 × 21111112。注意小数点后第一位必须为1,所以一共有9位小数。
算法竞赛入门经典(第2版)紫书 第三章数组和字符串 习题_第9张图片
这个数换算成十进制之后就是0.998046875263=9.2053576383452941018。你的任务是根据这个最大浮点数,求出阶码的位数E和尾数的位数M。输入格式为AeB,表示最大浮点数为A*10B。0

输入
输入文件包含大约300行输入。每一行包含一个浮点数f,它表示某个浮点类型可以表示的最大值。浮点数以十进制指数格式表示。因此,数字aeb实际上表示包含“0e0”的a×10^b. a线终止输入的值。a的值将满足约束0 < a < 10,并且小数点后正好有15位数字。
输出
每行输入输出产生一行输出。这一行包含m和e的值,您可以假设每个输入(除了最后一个)都有一个可能且唯一的解决方案。你也可以假设输入是这样的,m和e的值将遵循约束:9 ≥ m ≥ 0和30 ≥ e ≥ 1。也不需要假设(m + e + 2)是8的倍数。

Sample Input
5.699141892149156e76
9.205357638345294e18
0e0

Sample Output
5 8
8 6

你可能感兴趣的:(算法竞赛入门经典(第2版)紫书)