基础知识专题

一、字符串与大模拟

典例 A1138 Recover the Smallest Number -字符串

题目大意:给出一系列的数字进行组合,求数字最小的组合方法
思想:利用 cmp 函数,return a+b < b+a 字符串拼接比较,而不用找规律。
代码:


int n;
vector<string> vec;

bool cmp(const string& a,const string& b) {
	return a+b < b+a;
}
string ans;
int main() {
	cin >> n;
	getchar();
	vec.resize(n);
	for(int i = 0; i < n; ++i)	cin >> vec[i];
	sort(vec.begin(),vec.end(),cmp);
	for(int i = 0; i < n; ++i)	ans += vec[i];
	if(ans.find_first_not_of('0') == string::npos)	ans = "0";
	else ans = ans.substr(ans.find_first_not_of('0'));
	cout << ans << endl;
	return 0;
}
典例 A1140 Longest Symmetric String -字符串

题目大意:求最长的回文串的长度。
思路:中心枚举法,分奇数长度和偶数长度枚举。
代码:

string str;
int ans;
int main() {
	getline(cin,str);
	int i,j;
	for(int k = 0; k < str.size(); ++k) {		// 枢轴位置
		int cnt = 1;
		// 奇数长度
		i = k-1,j=k+1;
		while(i>=0 && j<str.size() && str[i]==str[j]) {
			cnt += 2;
			--i,++j;
		}
		ans = max(ans,cnt);
		
		// 偶数长度
		cnt  = 0;
		i = k,j = k+1;
		while(i>=0 && j < str.size() && str[i]== str[j]){
			cnt += 2;
			--i,++j;
		} 
		ans = max(ans,cnt);
	}
	printf("%d\n",ans);
	return 0;
}

PAT中也有很多大模拟的题目,总结就是:不怕用暴力,但有条理的暴力,可以一次一次遍历,但最忌讳条理不清,贪图一次解决!

典例 A1153

题目大意:给出⼀组学⽣的准考证号和成绩,准考证号包含了等级(⼄甲顶),考场号,⽇期,和个⼈编号信息,并有三种查询⽅式
查询⼀:给出考试等级,找出该等级的考⽣,按照成绩降序,准考证升序排序
查询⼆:给出考场号,统计该考场的考⽣数量和总得分
查询三:给出考试⽇期,查询改⽇期下所有考场的考试⼈数,按照⼈数降序,考场号升序排序

分析:先将所有的 card 字符串与对应的 score 存下来,然后每次遍历一遍,将满足条件的 card 装入 vector,再对 vector 进行排序。
技巧:统计第 i 考场的考生数量,然后按考生数对考场排序。在输入的时候,可以直接用 i 作为索引,那排序之后不就没了吗?可以用map,对 map 进行遍历,就可以获得 first、second。
代码:

#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#include<vector>
#include<map>
#include<unordered_map>
using namespace std;
typedef pair<int,int> PII;

int n,m;
struct node {
	string card;
	int score;
} nodes[10010];

/*
整体思路:
	思路清晰地暴力,不要害怕暴力
	先存下所有的信息,一遍遍历进行挑选

*/

bool cmp(const node& a,const node& b) {

	if(a.score != b.score)	return a.score > b.score;
	return a.card < b.card ;
}

int main() {
	

	scanf("%d%d",&n,&m);
	for(int i = 0; i < n; ++i) {
		cin>>nodes[i].card>>nodes[i].score;
	}

	int type;
	string s;
	for(int i = 1; i <= m; ++i) {
		cin>>type>>s;
		printf("Case %d: %d %s\n",i,type,s.c_str());

		vector<node> ans;
		if(type == 1) {			// 将该等级下的所有人输出
			for(int j = 0; j < n; ++j) {
				if(nodes[j].card[0] == s[0])	ans.push_back(nodes[j]);
			}
			sort(ans.begin(),ans.end(),cmp);

			if(ans.size() == 0 ) printf("NA\n");
			else for(int j = 0; j < ans.size(); ++j) {
				printf("%s %d\n",ans[j].card.c_str(),ans[j].score);
//					cout<<ans[j].card<<" "<<ans[j].score<<endl;
				}
		} else if(type == 2) {	// 统计该考场的人数与总分
			int cnt = 0,cnts = 0;

			for(int j = 0; j < n; ++j)	if(nodes[j].card.substr(1,3) == s) {
					cnt++;
					cnts+=nodes[j].score;
				}
			if(cnt == 0)	printf("NA\n");
			else printf("%d %d\n",cnt,cnts);
		} else {	
						// 统计某一天所有的考场以及人数
			unordered_map<string,int> mp;
			for(int j = 0; j < n; ++j) {			// 先统计出所有的考场
				if(nodes[j].card.substr(4,6) == s)	mp[nodes[j].card.substr(1,3)]++;
			}
			for(auto it = mp.begin(); it != mp.end(); ++it) {
				ans.push_back(node {it->first,it->second});
			}
			sort(ans.begin(),ans.end(),cmp);
			if(ans.size() == 0 ) printf("NA\n");
			else for(int j = 0; j < ans.size(); ++j) {
				printf("%s %d\n",ans[j].card.c_str(),ans[j].score);
//					cout<<ans[j].card.substr(0,3)<<" "<<ans[j].score<<endl;
				}

		}



	}


	return 0;
}
*典例 A1112 Stucked Keyboard

题目大意:键盘某些键卡住了,按⼀次重复k次,要求找出可能的键,并且输出正确的字符串顺序。可
能的键要求按照被发现的顺序输出。
注意:坏键一定会重复 k 次,那么有重复与不重复的,是好键。
思路:

  1. 找到不重复的键,一定不会是坏键
  2. 找重复 k 次的键,是可能的坏键
  3. 在可能坏键中去除一定不会的键

代码:

#include<bits/stdc++.h>
using namespace std;

int k;
string str;
map<char,bool> notBroken;				// 只改 true  不改 false
vector<char> ans;
map<char,bool> mp;
bool check(int i) {			// 判断 i 位置可不可能是 broken
	if(i == str.size()-1)	return false;
	for(int j = 1; j < k; ++j) {
		if(str[i+j] != str[i])	return false;
	}
	return true;
}

int main() {

	scanf("%d",&k);
	cin>>str;
//	cout<<str<<endl;
	int i = 0;
	while(i < str.size()) {
		if(check(i) == false)	{
			notBroken[str[i]] = true;
			++i;
		} else {
//			printf("%c",str[i]);
			if(find(ans.begin(),ans.end(),str[i]) == ans.end())
				ans.push_back(str[i]);
			i += k;
		}
	}
	for(int i = 0; i < ans.size(); ++i) {
		if(notBroken[ans[i]] == false) {
			printf("%c",ans[i]);
			mp[ans[i]] = true;
		} else ans.erase(ans.begin()+i);
	}
	printf("\n");
	i = 0;
	while(i < str.size()) {
		printf("%c",str[i]);
		if(mp[str[i]])	i += k;
		else ++i;

	}

	return 0;
}

科学计数法

典例 1073 Scientific Notation - 科学计数法

Sample Input 1:

+1.23400E-03

Sample Output 1:

0.00123400
结尾无空行

Sample Input 2:

-1.2E+10
结尾无空行

Sample Output 2:

-12000000000

题目大意:科学计数转普通数字。
分析:直接输出,可以不用构造 ans 字符串暂存。
代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<map>
#include<vector>
#include<queue>
#include<string>
using namespace std;

string str;
int main() {

	cin >> str;
	if(str[0] == '-')	cout << "-";
	int pos = str.find('E');
	int e = stoi(str.substr(pos+1));
	string num = str.substr(1,pos-1);

	if(e > 0) {		// 小数点右移
		cout << str[1];		// 整数部分
		int i = 0,cnt;
		for(i = 2,cnt = 0; i < num.size() && cnt++ < e; ++i)	cout << num[i];
		if(i == num.size()){
			for(int j = 0;j < e - cnt;++j)	cout << "0";
		}else{
			cout << ".";
			for(;i < num.size();++i)	cout << num[i];
		}
	} else {
		cout << "0.";
		for(int i = 0; i < abs(e)-1; ++i)	cout << "0";
		for(int i = 0; i < num.size(); ++i)	if(num[i] != '.')	cout << num[i];
	}

	return 0;
}
典例 A1060 Are they equal - 科学计数法

Sample Input 1:
3 12300 12358.9

Sample Output 1:

YES 0.123*10^5

Sample Input 2:

3 120 128

Sample Output 2:

NO 0.12010^3 0.12810^3

题目大意:普通数字转科学计数法:判断是否相等。将数字表示成 0. d [ 1 ] . . . d [ N ] ∗ 1 0 k 0.d[1]...d[N]*10^k 0.d[1]...d[N]10k,给出位数 N ,判断两数是否相等。
分析:有条理的分类与字符串处理。

代码:

#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#include<vector>
#include<map>
using namespace std;

int n;
string a,b;
struct node {
	string num;
	int e;
};

// 将字符串转换为科学表达式
node change(const string& str) {
	string s(str);
	// 为 0 的特殊情况
	if(s.find_first_not_of('0') == string::npos)	{
		string tmp(n,'0');
		return node {tmp,0};
	}

	s = s.substr(s.find_first_not_of('0'));			// 去除前导 0
//	cout << "s: "<<s<<endl;

	int dot = -1;		// 小数点的位置
	for(int i = 0; i < s.size(); ++i)
		if(s[i] == '.')	{
			dot = i;
			break;
		}

	if(dot == -1) {			// 没有小数点的情况
		int t = s.size();
		if(s.size() < n) s.insert(s.end(),n-s.size(),'0');
		return node {s.substr(0,n),t} ;
	} else {
		if(s[0] == '.') {			// < 1 的小数,小数点右移
//		cout <<"enter "<<endl;
			s = s.substr(1);
			// 为 0.0 的情况 
			if(s.find_first_not_of('0') == string::npos)	{
				string tmp(n,'0');
//				cout <<"tmp:"<<tmp<<endl;
				return node {tmp,0};
			}
			// 找到第一个非 0 的位置 
			int pos;
			for(pos = 0; pos < s.size(); ++pos)	if(s[pos] != '0')	break;
			int e = -1 * pos;
			s = s.substr(pos);
			if(s.size() < n)	s.insert(s.end(),n - s.size(),'0');
			return node {s.substr(0,n),e} ;
		} else {				// > 1 的小数,小数点左移
		
			int e = dot - 0;
			
			s = s.erase(dot,1);		// 擦去小数点 
			if(s.size() < n)	s.insert(s.end(),n - s.size(),'0');
			return node {s.substr(0,n),e} ;
		}
	}
}

int main() {

	cin >> n;
	cin >> a >> b;
	node na(change(a));
	node nb(change(b));
	if(na.e == nb.e && na.num==nb.num) {
		printf("YES 0.%s*10^%d\n",na.num.c_str(),na.e);
	} else printf("NO 0.%s*10^%d 0.%s*10^%d",na.num.c_str(),na.e,nb.num.c_str(),nb.e);


	return 0;
}

二、哈希

哈希基础
冲突解决:
H ( k e y ) = ( H ( k e y ) + d i ) % M S i z e H(key) = (H(key)+d_i)\%MSize H(key)=(H(key)+di)%MSize,按照 di 的不同分为

  1. d i = 1 d_i=1 di=1,线性探测
  2. d i = ± i 2 d_i=±i^2 di=±i2,平方探测,先加后减两次
  3. d i = i ∗ H 2 ( k e y ) d_i=i*H_2(key) di=iH2(key),双散列

平均查找长度ASL
查找 成功(查找到目标) 或 失败(遇空位) 的查找次数 :

  1. 成功:在表中,逐一计算比较次数求平均
  2. 失败:不在表中,针对 每个关键字 计算查找失败的比较次数(到空位的距离,或整个表长)求平均。

思想:
1 0 5 10^5 105 以内的数字,存储在 hash 表中,这样可以实现随机访问。尤其是给出ID值的题目,直接用ID作为数组下标。

例题:

典例 A1116 Come on! Let’s C - 素数判断

题目大意:给出一组选手的ID,以及对应的名次,冠军获得神秘礼物,奇数排名获得小黄人,其他获得巧克力。输入ID,输出奖励结果。
思路:

  • 用 ID 作为索引,建 hash 表
  • 判断素数:普通遍历法

代码:

#include<cstdio>
#include<algorithm>
#include<cmath>

using namespace std;


int award[10000];			// hash 表,记录名次
int vis[10000];			// 访问表,表示已经访问

// 质数判断
bool isPrime(int x) {
	if(x == 1)	return false;

	int sx = sqrt(x);
	for(int i = 2; i <= sx; ++i) {
		if(x % i == 0	)	return false;
	}
	return true;
}

int main() {

	int n,k;

	int tmp;
	scanf("%d",&n);
	for(int i = 1; i <= n; ++i) {
		scanf("%d",&tmp);
		award[tmp] = i;
	}

	scanf("%d",&k);
	for(int i = 1; i <= k; ++i) {
		scanf("%d",&tmp);
		if(award[tmp] == 0) {
			printf("%04d: Are you kidding?\n",tmp);
		} else  if(vis[tmp]) {
			printf("%04d: Checked\n",tmp);
		} else {
			if(award[tmp] == 1) {
				printf("%04d: Mystery Award\n",tmp);
			} else if(isPrime(award[tmp])) {
				printf("%04d: Minion\n",tmp);
			} else {
				printf("%04d: Chocolate\n",tmp);
			}
			vis[tmp] = 1;
		}

	}


	return 0;
}
典例 A1145 Hashing - Average Search Time- 哈希冲突解决

题目大意:给定⼀个序列,⽤平⽅探测法解决哈希冲突,建立哈希表。然后给出 m 个数字,如果这个数字不能够被插⼊就输出”X cannot be inserted.”,然后输出这 m 个数字的平均查找时间。(哈希冲突使用正向平方探测)
知识回顾:哈希冲突的解决办法:

1.开放定址法:

  • 线性探查法: x = ( H ( x ) + 1 ) % T S I Z E x = (H(x)+1) \%TSIZE x=(H(x)+1)%TSIZE
  • 平方探测法: x = ( H ( x ) ± d i 2 ) % T S I Z E x = (H(x)±di^2)\%TSIZE x=(H(x)±di2)%TSIZE,其中 0 < d i < = T S i z e − 1 0<di<=TSize-1 0<di<=TSize1

可以直接用循环解决,不用谢函数

for(int j = 0;j < TSize;++j)	{	// hash 探测 
	if(hashTable[(tmp%TSize+j*j)%TSize] == 0){
		hashTable[(tmp%TSize+j*j)%TSize] = tmp;
	}
}

2.拉链法

坑点

  1. 计算平均比较次数时,遇到空白,应当提前结束,而不用一直取 di 到 TSize-1
  2. 在插入时,取 di <= TSize-1, 因为取 TSize 时会回到原点。但是在查找时,根据用例是需要计算到 TSize 的,不知为何。

代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;

int n,m,TSize;

bool isPrime(int x){
	if(x == 1)	return false;
	if(x == 2)	return true;
	for(int i = 2;i <= sqrt(x);++i){
		if(x % i == 0)	return false;
	}
	return true;
}

int hashTable[MAXN];	
int tmp;		
int main(){
	scanf("%d%d%d",&TSize,&n,&m);
	while(isPrime(TSize) == 0)	TSize++;
//	printf("TSIze: %d\n",TSize);
	
	for(int i = 0;i < n;++i){				// 处理 n 个数据 
		scanf("%d",&tmp);
		int ok = 0;
		for(int j = 0;j < TSize;++j)	{	// hash 探测 
			if(hashTable[(tmp%TSize+j*j)%TSize] == 0){
				hashTable[(tmp%TSize+j*j)%TSize] = tmp;

				ok = 1;
				break;
			}
		}
		if(!ok){
			printf("%d cannot be inserted.\n",tmp);
		}
	}
	int ans = 0;
	for(int i = 0;i < m;++i){				//  进行探查
		scanf("%d",&tmp) ;
		
		int j;
		for( j = 0;j < TSize;++j){

			if(hashTable[(tmp%TSize+j*j)%TSize] == tmp || hashTable[(tmp%TSize+j*j)%TSize] == 0){
				break;
			}
		}		
		ans += j+1;

	}
	printf("%.1lf\n",ans*1.0/m);
	
	return 0;
}
典例 A1149 Dangerous Goods Packaging- <int,vecotor>

题目大意: 给出互相冲突的物品列表,再给出 m 个序列,判断每个序列是否合法没有相冲。
分析:

  1. 用 unordered_map<LL.bool>, 将第一个物品 id1*10000+id2 作为索引,压缩空间: m a p < i d 1 ∗ 10000 + i d 2 , b o o l map<id1*10000+id2,bool map<id110000+id2,bool
  2. m a p < i n t , v e c t o r < i n t > > map<int,vector< int> > map<int,vector<int>> 每个物品映射一个 vector,双重循环暴力判断。

代码(方法2):

#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
#define MAXN 100000
using namespace std;

map<int,vector<int> > mp;		// 每个物品,对应一个违禁列表

int n,m;
int a,b;
int main() {

	scanf("%d%d",&n,&m);
	for(int i = 0; i < n; ++i) {
		scanf("%d%d",&a,&b);
		mp[a].push_back(b);
		mp[b].push_back(a);
	}



	int k;
	for(int i = 0; i < m; ++i) {
		
		int vis[MAXN] = {0};
		scanf("%d",&k);
		vector<int> v(k);

		for(int j = 0; j < k; ++j) {
			scanf("%d",&v[j]);
			vis[v[j]] = 1;
		}

		int flag = 1;
		// 双重循环 暴力判断
		for(int u = 0; u < k; ++u) {
			for(int s = 0; s < mp[v[u]].size(); ++s) {
				// u 的第 v 个相冲的物品 : mp[v[u]]
				if(vis[mp[v[u]][s]] == 1) {
					flag = 0;
					break;
				}
			}
			if(!flag) break;
		}
		
		
		printf("%s\n",flag?"Yes":"No");

	}



	return 0;
}

三、逻辑题与数学

例题:

典例 A1117 Eddington Number

题目大意:给出一个序列,满⾜有E天骑⻋超过E英⾥的最⼤整数E。( 1 0 5 10^5 105)

我的做法:hash 表存下英里对应的天数,从大到小枚举 E 的取值,从 E+1 开始累加。

注意点:超过 more than,需要大于。

Sample Input:
10
6 7 6 9 3 10 8 2 7 8

Sample Output:

6

柳婼做法:里程从大到小排序,从一个元素开始遍历,意为:第 1 天是否超过了 1 英里,… 第 i 天是否超过了 i 英里。终止的地方即为答案。

分析:里程 a[i] 降序排列,日期 i 升序排列,这样保证了 a[i] > i 时,所有的 a[0…i-1] > a[i] > i 。

#include <iostream>
#include <algorithm>
using namespace std;
int a[1000000];
int main() {
 int n, e = 0;
 scanf("%d", &n);
 for(int i = 0; i < n; i++)
 scanf("%d", &a[i]);
 
 sort(a, a+n, greater<int>());
 while(e < n && a[e] > e+1) e++;
 
 printf("%d", e);
 return 0; }

1. 反向思维

典例 A1148 Werewolf - Simple Version- 反向推导

链接
题目大意:给出人数 n ,每个人有一个陈述。共有两狼,其中两个人说了假话:一狼一人,求出这两狼,若有多重情况,输出字典序最小的。
分析:

  • 正向思维:通过假设判断的方法,假定两个人说的是假话,然后讲他们说的话改为真话,再与其他的条件合并…这也太复杂了吧?
  • 反向思维:假定两个人为狼(则剩下的是人);根据假定的结果,判断谁说了假话;判断说假话的人是否是两个人,是否是一狼一人?

反向推导的重要特性已知结论的要求十分清晰!
例如:本题,已知的结论:是两个狼,要求:出两狼;限制:两人说假话,其中一狼一人。要求的十分清楚而且固定,则直接假设结论,然后反推,是否满足限制条件。

代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define MAXN 110
using namespace std;

/*
思路:	最后的状态是 : 2狼;2人说假话,其中 1 狼 1 村民
		最后的任务是 : 找出 2 狼

反推: 先固定下 2 狼,去判断他们谁说的是真话,最后说假话的人 1.人数 = 2?2. 1 狼 1 人?

*/

int a[MAXN];			// 假设数组,
int b[MAXN];			// 记录发言
int n;
vector<int> ans;
int main() {

	scanf("%d",&n);
	for(int i = 1; i <= n; ++i)	scanf("%d",&b[i]);

	// 开始假设: 由于 是顺序遍历,则第一个满足条件的就是最小的 
	for(int  i = 1; i <= n; ++i)
		for(int j = i+1; j <= n; ++j) {
			// 假定 i 和 j 是狼,初始化标记数组 
			fill(a,a+MAXN,1);
			a[i] = a[j] = -1;

			vector<int> tmp;			// 存放这一轮说假话的人
			for(int k = 1; k <= n; ++k) {
				if(a[abs(b[k])] * b[k] < 0)	tmp.push_back(k);
			}

			if(tmp.size() != 2)	continue;
			if(a[tmp[0]] * a[tmp[1]] < 0)	{
				// 满足一狼一人的情况,
				printf("%d %d\n",i,j);
				return 0;
			}
		}

	printf("No Solution\n");

	return 0;
}
典例 A1093 Count PAT’s - 数学

链接
题目大意:给出字符串,APPAPT,求出能构成几个 “PAT”。
分析:以 “A” 为分割,该 “A” 能构成的 PAT 个数为 左边 P 的个数 * 右边 T 的个数。遍历所有的 A 进行累加,同时动态维护 左A、 右T 的数量。
P 个数置 p=0,T 个数使用 count 函数统计 = t;遇 P ,p++,遇 T t–。

代码:

#include<cstdio>
#include<iostream>
#define mod 1000000007
using namespace std;

string str;
int ans;
int p;
int main() {

	cin>>str;
	
	int t = count(str.begin(),str.end(),'T');
	
	for(int i = 0;i < str.size();++i){
		if(str[i] == 'P')	p++;
		else if(str[i] == 'T')	t--;
		else ans = (ans + p % mod * t % mod) % mod;
	}
	printf("%d\n",ans);

	return 0;
}
典例 A1104 Sum of Number Segments - 数学

链接
题目大意: { 0.1, 0.2, 0.3, 0.4 }, we have 10 segments: (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) and (0.4).
分析:

方法一:类似上一题数 PAT 的个数,计算每个元素可能出现的次数,即枚举包含该数的区间的 左边的起点 和 右边的终点 。
即 :包含该元素的区间的个数,设 i (≥1) 为该数位置, p 为区间起点,q 为区间终点。则 q 的可能取值为 该数及该数左边的数字个数,即 i;q 为区间终点位置,为该数及该数右边的数字个数,即 (n-i+1)。
代码:


int n;
long double ans;
long double a[MAXN];
int main() {

	scanf("%d",&n);
	long double tmp=0;
	
	for(int i = 1;i <= n;++i){
		scanf("%Lf",&tmp);
		ans += tmp * i * (n-i+1) ;
		
	}
	printf("%.2Lf\n",ans);

	return 0;
}

方法二:找规律。

0.1
0.1+0.2
0.1+0.2+0.3
0.1+0.2+0.3+0.4           
          0.2+0.3
          0.2+0.3+0.4
                    0.3
                    0.3+0.4

代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<queue>
#define MAXN 100010
using namespace std;

int n;
long double ans;
long double a[MAXN];
int main() {

	scanf("%d",&n);
	long double tmp=0;
	long double t = 0;
	for(int i = 0; i < n; ++i)	{
		scanf("%Lf",&a[i]);
		t += a[i];
		tmp += t;
	}
	
//	sort(a,a+n);
	
	ans = tmp;
	for(int i = 1; i < n; ++i) {		// 外层遍历起点
		tmp -= (n-i+1) * a[i-1];
		ans += tmp;
	}

	printf("%.2Lf\n",ans);

	return 0;
}

2.阶乘末尾零的个数

阶乘后的零
题目大意:求 n! 后有几个 0 ?
分析:
0 由 2*5=10 得来,故统计 2*5 的二元组个数。

观察可发现: 10 ∗ 9 ∗ 8 ∗ 7 ∗ 6 ∗ 5 ∗ 4 ∗ 3 ∗ 2 ∗ 1 10*9*8*7*6*5*4*3*2*1 10987654321可以分解为 ( 2 ∗ 5 ) ∗ 9 ∗ ( 2 ∗ 4 ) ∗ 7 ∗ ( 2 ∗ 3 ) ∗ ( 5 ∗ 1 ) ∗ ( 2 ∗ 2 ) ∗ 3 ∗ ( 2 ∗ 1 ) ∗ 1 (2*5)*9*(2*4)*7*(2*3)*(5*1)*(2*2)*3*(2*1)*1 (25)9(24)7(23)(51)(22)3(21)1
因子含 5 的数每 5 个出现一次,而因子含 2 的数每 2 个出现一次,说明 2 的个数一定 大于等于 5 的个数,则统计 2*5 的二元组个数转变为,统计 5 的个数。

又经观察发现: ( 5 ∗ 5 ) ∗ 24 ∗ 23 ∗ 22 ∗ 21 ∗ ( 4 ∗ 5 ) ∗ . . . 1 (5*5)*24*23*22*21*(4*5)*...1 (55)24232221(45)...1,每隔5 个出现 1 个5,而每隔 25 个出现两个 5,类推,每隔 125 个会出现 3 个 5。。。

问题转换为统计 n/5 + n/25 + n/125+ n/X…的值。(重复的 5 已经被计算了,例如 每隔25 个 出现两个 5, 而第一个5 已经在间隔为 5 的计算中被统计了,所以只用算一次;同理 125 的 3 个 5分别在区间为 5、25 中被计算了 2 个,只用算一次)。
而此计算显然是一个迭代的过程。

class Solution {
public:
    int trailingZeroes(int n) {
        int ans= 0;
        while(n){
	        ans += n / 5;   
            n /= 5;
        }
        return ans;
    }
};

3.欧拉回路

欧拉回路:如果图G中所有边一次仅且一次行遍所有顶点的回路称作欧拉回路。
条件:奇度顶点数为 0

欧拉通路:如果图G(有向图或者无向图)中所有边一次仅且一次行遍所有顶点的通路称作欧拉通路。
条件:奇度顶点数为 0 或 2.

典例 7-32 七桥问题

链接
代码:


int n,m;
int a,b;
int degree[MAXN];

int k;

int father[MAXN];
void init() {
	for(int i = 0; i <= n; ++i)	father[i]= i;
}

int findFather(int x) {
	int a = x;
	while(x!=father[x])	x = father[x];
	while(a != x) {
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

void merge(int a,int b) {
	int fa = findFather(a);
	int fb = findFather(b);

	if(fa != fb)	{
		father[fa] = fb;
	}
}

int main() {

	scanf("%d%d",&n,&m);
	init();
	for(int i = 0; i<m; ++i) {
		scanf("%d%d",&a,&b);
		merge(a,b);
		degree[a]++;
		degree[b]++;
//		printf("fa : %d fb: %d\n",father[a],father[b]);
	}
	int cnt = 0;
	int root = 0;
	for(int i = 1; i<=n; ++i)	{
		if(degree[i]%2)	++cnt;
		if(i == father[i])++root;
	}
	printf("%d\n",cnt == 0 && root == 1);

	return 0;
}

四、链表、栈、队列

PAT中的链表常见用法总结如下:
1.给出无序的结构体,需要 遍历链表 形成有序(必考)。
2.根据不同要求,对链表进行拆分与重排的处理,又分为:

  • 排序法。给字段赋值,利用 sort 完成排序。(较多)
  • 模拟法。再开 vector,模拟链表的处理。

3.通常会给出多余的节点,注意需要筛除。(坑点)。

典例 A1097 Deduplication on a Linked List - 排序法

题⽬⼤意:给⼀个链表,去重(去掉值或者绝对值相等的),先输出删除后的链表,再输出删除了的
链表。
思路:c++11中可以在定义时赋初始值,添加 num 字段,首次出现设为 cnt,重复出现设为 maxn+cnt1,其余未出现设为 2*maxn,sort 排序即可。
代码:

#include<bits/stdc++.h>
#define MAXN 0x3f3f3f3f
using namespace std;

int head,n;
struct node {
	int data;
	int add;
	int next;
	int num = 2*MAXN;
};

bool cmp(const node& a,const node& b){
	return a.num < b.num;
}

vector<node> vec(100000);
map<int,bool> mp;
int add;
int main() {

	scanf("%d%d",&head,&n);
	for(int i = 0; i < n; ++i) {
		scanf("%d",&add);
		scanf("%d%d",&vec[add].data,&vec[add].next);
		vec[add].add = 	add;
	}

	int cnt = 0;
	int cnt1 = 0;
	while(head != -1) {
		if(mp[abs(vec[head].data)] == false) {
			vec[head].num = cnt++;
			mp[abs(vec[head].data)] =true;
		}else {
			vec[head].num = MAXN + cnt1++;
		}
		head = vec[head].next;
	}
	
	sort(vec.begin(),vec.end(),cmp);
	
	for(int i = 0;i < cnt1+cnt;++i){
		if(i == cnt-1 || i == cnt1 + cnt - 1)	printf("%05d %d -1\n",vec[i].add,vec[i].data);
		else printf("%05d %d %05d\n",vec[i].add,vec[i].data,vec[i+1].add);
	}

	return 0;
}
典例 A1051 Pop Sequence - 判断合法出栈序列

链接
题目大意:按照 1,2,…n的顺序入栈,判断给出的出栈序列是否合法。
模板入栈一个元素,判断栈顶是否与当前出栈序列相等,相等则循环出栈。最后判断成功出栈的个数 是否等于 出栈序列长度。

模板代码:

	stack<int> st;
		vector<int> vec(n);			// 出栈序列
		for(int j  = 0; j < n; ++j)	scanf("%d",&vec[j]);

		int cnt = 0;

		for(int i = 1; i <= n; ++i) {
			st.push(i);
			if(st.size() > m)	break;			// 栈的大小限制

			while(!st.empty() && st.top() == vec[cnt]) {		// 循环出栈
				st.pop();
				++cnt;
			}
		}

		printf("%s\n",cnt == n ? "YES":"NO");					// 合法序列判断

完整代码:

#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#include<vector>
#include<map>
#include<cstring>
#include<set>
#include<stack>
#define MAXN 100010
using namespace std;


int m,n,k;
int main() {


	scanf("%d%d%d",&m,&n,&k);
	while(k--) {
		stack<int> st;
		vector<int> vec(n);
		for(int j  = 0; j < n; ++j)	scanf("%d",&vec[j]);

		int cnt = 0;

		for(int i = 1; i <= n; ++i) {
			st.push(i);
			if(st.size() > m)	break;

			while(!st.empty() && st.top() == vec[cnt]) {
				st.pop();
				++cnt;
			}
		}

		printf("%s\n",cnt == n ? "YES":"NO");
	}


	return 0;
}

五、排序

典例 A1101 Quick Sort - 快速排序

链接
题目大意:给出一个序列,判断可能是枢轴的数字(左边都小于它,右边都大于它)。
思路:
方法一:maxF[i] 正序统计 [0,i] 的最大值, minF[i] 逆序统计 [i,n)的最小值,比较 (a[i] > maxF[i-1] && a[i] < minF[i+1])
方法二:快速排序的核心思想 - 一次将一个数字排序到位。那么将原序列排序,位置不变的数可能是枢轴。
注意特殊情况:{5,4,3,2,1} 与 {1,2,3,4,5},3 的位置不变但不是枢轴。
故:左右的个数相等时,需要加一个变量来判断是否满足前面的最大值小于它。

为什么只判断最大值呢?不判断最小值?
答:假如其(如3)后面有比它小的数,由于排序后位置不变,则其前面必然有比它大的数,且数目相等,找到了前面有比它大的数,则一定说明其后有比它小的数。

代码:
方法一:

#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
#include<set>

#define MAXN 100010
using namespace std;

int n;
int a[MAXN];
set<int> st;
int maxF[MAXN];			// 【0,i】 的最大值
int minF[MAXN];			// [i,n-1]	的最小值
int main() {

	scanf("%d",&n);
	int cnt = 0;
	maxF[0] = 0;
	for(int i = 1; i <= n; ++i)	{
		scanf("%d",&a[i]);
		maxF[i] =  max(maxF[i-1],a[i]);
	}
	minF[n+1] = 0x3f3f3f3f;
	for(int i = n; i >= 0; --i) {
		minF[i] = min(minF[i+1],a[i]);
	}

	for(int i = 1; i <= n; ++i) {
		if(a[i] < maxF[i-1] || a[i] > minF[i+1])	continue;
		st.insert(a[i]);
		++cnt;
	}

	printf("%d\n",st.size());
	if(st.empty())printf("\n");
	else
		for(auto it = st.begin(); it != st.end(); ++it)	{
			--cnt;
			printf("%d%s",*it,cnt > 0 ? " ":"");
		}

	return 0;
}

方法二:

#include<bits/stdc++.h>
using namespace std;

int n;
vector<int> a,b;
set<int> st;
int main(){
	
	scanf("%d",&n);
	a.resize(n);b.resize(n);
	for(int i = 0;i < n;++i)	scanf("%d",&a[i]);
	b = a;
	
	sort(b.begin(),b.end());
//	for(int i = 0;i < n;++i) printf("%d%s",a[i],i<n-1?" ":"\n");
//	for(int i = 0;i < n;++i) printf("%d%s",b[i],i<n-1?" ":"\n");
	
	int cnt = 0;
	int maxF = 0;
	for(int i = 0;i < n;++i){
		if(a[i] == b[i] && a[i] >= maxF){
			++cnt;
			st.insert(a[i]);
		}
		maxF = max(maxF,a[i]);
	}
	
	printf("%d\n",cnt);
	if(!cnt)	printf("\n");
	else{
		for(auto it = st.begin();it != st.end();++it){
			printf("%d%s",*it,--cnt>0?" ":"\n");
		}
	}
	
	
	
	return 0;
}
典例 A1089 Insert or Merge - 归并排序

题目大意:给出两个序列,判断是插入还是归并,并给出下一次排序序列。
思路:模拟归并划分区间,用 sort 简化。

#include<cstdio>
#include<algorithm>
#include<vector>
#define MAXN 110
using namespace std;


int n;
vector<int> a,b;
vector<int> ins;		// 插入排序辅助数组

int main() {

	scanf("%d",&n);
	a.resize(n);
	b.resize(n);
	ins.resize(n);
	for(int i = 0; i < n; ++i)	scanf("%d",&a[i]);
	ins = a;
	for(int i = 0; i < n; ++i)	scanf("%d",&b[i]);

	// 模拟插入排序
	int flag = 1;
	int i = 2;					// 插入排序从第二个元素开始,又,是排过序的,所以当前应该排 第三个
	do {

		if(ins == b) {			// 满足条件了
//			if(i > 1)	
			flag = 0;			// 修改循环条件
//			else break;
		}

		// 模拟插入排序
		sort(ins.begin(),ins.begin() + i + 1);

		if(!flag) {				// 满足条件时输出
			printf("Insertion Sort\n");
			for(int j = 0; j < n; ++j)	{
				printf("%d%s",ins[j],j < n-1?" ":"\n");
			}
			return 0;
		}
		++i;
	} while(flag && i < n);

	// 没有返回,则是归并排序, 直接拿 a 数组操作
	printf("Merge Sort\n") ;
	flag = 1;
	int step = 2;			// 归并的步长
	do {

		if(a == b) {
			flag = 0;
		}
		// 模拟归并排序
		for(int j = 0; j < n/step; ++j) {				// j 表示 分组数
			sort(a.begin()+j*step,a.begin()+(j+1)*step);
		}
		//对尾巴排序
		sort(a.begin()+n/step*step,a.end());

		// 满足条件时输出
		if(!flag) {
			for(int j = 0; j < n; ++j)	{
				printf("%d%s",a[j],j < n-1?" ":"\n");
			}
			return 0;
		}
		step *= 2;
	} while(flag);


	return 0;
}

六、BFS、DFS

典例 A1091 Acute Stroke - 三维BFS

链接
题目大意:给出一个三维图,找出数目大于 v 的连通域。

分析:可用DFS和BFS,DFS找所有路径,BFS找最短路径,这里只是遍历。考虑到要统计连通域的大小,在DFS中可以用参数传递当前体积,但何时停止、何时累加十分不变。可以使用BFS,一次BFS可直接循环中入队的个数即可。
代码:

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

int m,n,l,t;
int G[1300][130][62];			// 三维 图
int inq[1300][130][62];

struct node {
	int a,b,c;
	node& operator = (const node& s) {
		a = s.a;
		b = s.b;
		c = s.c;
	}
};

int dir[6][3] = {{0,-1,0},{-1,0,0},{0,1,0},{1,0,0},{0,0,1},{0,0,-1}};

int ans;
void bfs(int a,int b,int c) {

	// 0. 构造,初始节点入队
	queue<node> qu;
	qu.push(node{a,b,c});
//	printf("qu.front : %d %d %d\n",qu.front().a,qu.front().b,qu.front().c) ;
	inq[a][b][c] = 1;

	int cnt = 0;
	while(!qu.empty()) {			// 对不空时循环
		// 2.出兑一个元素,操作
		node top = qu.front();
		qu.pop();
//		printf("%d %d %d\n",top.a,top.b,top.c);
		++cnt;

		// 3.所有可走点入队
		int i,j,k;
		for(int s = 0; s < 6; ++s) {
			i = top.a + dir[s][0];
			j = top.b + dir[s][1];
			k = top.c + dir[s][2];
			
			if(G[i][j][k] && !inq[i][j][k]) {
				qu.push(node {i,j,k});
				inq[i][j][k] = 1;
//				printf("push %d %d %d\n",i,j,k);
			}
		}
	}

	if(cnt >= t)	ans += cnt;
}

int main() {

	scanf("%d%d%d%d",&m,&n,&l,&t);
	// 从 1 开始存储,不用做边界判断
	for(int k = 1; k <= l; ++k)
		for(int i = 1; i <= m; ++i)
			for(int j = 1; j <= n; ++j)
				scanf("%d",&G[i][j][k]);


	for(int k = 1; k <= l; ++k)
		for(int i = 1; i <= m; ++i)
			for(int j = 1; j <= n; ++j) {
				if(G[i][j][k] && !inq[i][j][k]) {
					bfs(i,j,k);
				}
			}

	printf("%d\n",ans);

	return 0;
}
典例 A1103 Integer Factorization - 数位DFS

题目大意:给出 N,k,p,求出 N = n [ 1 ] P + . . . n [ K ] P N = n[1]^P + ... n[K]^P N=n[1]P+...n[K]P 的情况。
分析:递归终点 N,深度 k 给定,数位DFS。从第 k 位开始,依次枚举小于等于当前位的数字向下递归。

难点:尤其是初始的深度 depth 设置、递归终点时 depth 的推算,初始节点参数的设置。

  • 在进入 DFS 前,depth,sum 参数应都设为 0,而不能是最大值。即 应是 d f s ( d e p t h = 0 , s u m = 0 , x = n ) dfs(depth = 0,sum = 0,x = n) dfs(depth=0,sum=0,x=n) 而不是 d f s ( d e p t h = 0 , s u m = n p , x = n ) dfs(depth = 0,sum = n^p,x = n) dfs(depth=0,sum=np,x=n),由于带了第一个数字进入递归,第一层是无法回退的;应当在进入 dfs 后在 for 循环中累加,这样下轮循环才可以回退与覆盖。
  • 第一个数字在 dfs 中处理,则第一次 dfs depth = 0+1,sum 更新;

七、STL的使用

1.set 的用法与重载

1.特性:

  • 自动排序(从小到大)
  • 不可更改先删除,再插入)

2.操作函数

  • 创建:set<TYPE,CMPCLASS> st;
  • 查找: set<>::iterator = st.find(node),可以构造临时结构体用于查找。(vector 不行)
  • 插入:st.insert(node)

3.运算符重载

  • 自定义 Mycmp 类,
  • Mycmp 类中
struct node{
	int data;		//数据
	int count;		//个数
	node(..){}
}
//比较函数
struct Mycmp{
	bool operator()(const nnode& a,const node& b){
		if(a.count!= b.count) return a.count> b.count;
		else return a.data < b.data;
	}	
}

定义
int main(){
	
	set<node,Mycmp> st;
	st.insert(node());

}

2.priority_queue 的用法与重载

1.特性:

  • 大根堆
  • 自定义排序与 sort 定义方式相反

思考一下,堆的堆首元素放在堆的末尾,要想最小的在堆顶,即最小的在末尾,对应满足 a >b 则 a 在前。

2.操作函数
-priority_queue<TYPE,vector<TYPE>,CMPCLASS> qu.

3.运算符重载


// 自定义数据结构
struct node{
	int data;
	int cnt;
	node(int _data,int _cnt):data(_data),cnt(_cnt){
	};
};

// 运算符重载
struct Mycmp{
// 预期目的:输出 cnt 最大的,否则输出 data 最大的
// cnt 最大,则cnt大的在后,小的在前,故应为 a,cnt < b.cnt

	bool operator()(const node& a,const node& b){
		if(a.cnt != b.cnt)	return a.cnt < b.cnt;
		else return a.data < b.data;
	}
};

priority_queue<node,vector<node>,Mycmp> qu;

int main() {
	
	
	qu.push(node(9,5));
	qu.push(node(10,4));
	qu.push(node(11,4));
	
	cout<<qu.top().data<<endl;
	qu.pop();
	cout<<qu.top().data<<endl;
	
}
典例 A1129- Recommendation System -在线查询

题目大意:根据⽤户每次点击的东⻄的编号,输出他在点当前编号之前应该给这个⽤户推荐的商品的编号,只推荐k个,也就是输出⽤户曾经点击过的商品编号的最多的前k个。如果恰好两个商品有相同
的点击次数,就输出编号较⼩的那个。
数据量: N <= 50,000,k <= 10

分析:每处理一个数字,就要统计其前面的数字的个数,是一个动态的过程,可以定义一个结构体,保存数字,以及该数出现的次数。由于计数会动态更新,所以再开一个固定数组,统计累计出现的个数。

  • 如果每处理一个,就排序一次,显然是会超时的,只能过16分。最好的方法是,借用更高效的数据结构,帮助我们自动的完成排序。那么可以考虑 set 和 priority_queue 这两个有自动排序功能的数据结构。
  • priority_queue,只能获取堆顶元素,只能弹出堆顶元素。由于每处理一个,前面的数字统计会变化,则非常难以处理。
  • set 自动排序,可用 erase / insert 来删除/插入,很适合。但要注意的是,set 中的数据不能修改,必须先删除,再插入。

代码:

#include<algorithm>
#include<cstdio>
#include<set>
#define MAXN 50010
using namespace std;

struct node{
	int data;
	int cnt;
	node(int _data,int _cnt):data(_data),cnt(_cnt){
	}
};

struct Mycmp{
	bool operator()(const node& a,const node& b){
		if(a.cnt != b.cnt)	return a.cnt > b.cnt;
		else return a.data < b.data;
	}
};


set<node,Mycmp> st;

int n,k;
int num[MAXN];
int main(){
	
	scanf("%d%d",&n,&k)	;
	int t;
	for(int i = 0;i < n;++i){
		scanf("%d",&t);
		if(i){
			// 1.先输出
			 auto it = st.begin();
			 int c = 0;
			 printf("%d:",t);
			 for(;c < k && it->cnt != 0;++it){
			 	printf(" %d",it->data);
			 	++c;
			 }
			 printf("\n");
			
			// 2.更新 set 中数字的个数
			auto itt = st.find(node(t,num[t])); // 先寻找有没有
			if(itt != st.end()){	// 若有,则先删除,再插入 
				st.erase(itt);
			} 
			// 没有的话,直接插入 
			st.insert(node(t,num[t]+1));
		}else{
			st.insert(node(t,1));
		}
		num[t]++;
	}
	
	
	return 0;
}

3. getline

C 字符串:

cin.getline(char* s,size _n 长度,char deilm 结束字符)

C++ string

getline(cin 输入流,str)

4.upper_bound二分

upper/lower_bound 的用法

在 [first,last) 查找第一个 大于 val 的 位置。否则返回 end()。

int upper_bound(int first,int last,int val)

在 [first,last) 查找第一个 大于等于 val 的 位置。否则返回 end()。

int lower_bound(int first,int last,int val)
二分法

1.查找具体值,成功返回位置,失败返回 -1

int binarySearch(int low ,int high,int val,int a[]){
	while(low < high){
		int mid = (high - low) / 2 + low;            避免溢出 !!
		if(a[mid] == val)	return mid;
		else if(a[mid] > val) high = mid;			// 在左区间寻找
		else low = mid + 1;							// 在右区间寻找
	}
	return -1;										// 没找到特定值,则返回
}

2.查找第一个 大于 val 的位置,成功返回位置,失败则返回应当插入的位置 (left) 而不是 -1

分析 :要寻找第一个大于 val 的位置,当 a [ m i d ] < = v a l a[mid] <= val a[mid]<=val 时,显然不是要找的,则应当使 l o w = m i d + 1 low = mid+1 low=mid+1;当 a [ m i d ] > v a l a[mid] > val a[mid]>val 时,其可能是,也可能不是,应当继续寻找更小的, h i g h = m i d high= mid high=mid

边界分析:由于是向下取整,最坏的情况是 h i g h = l o w + 1 high = low+1 high=low+1,而 high 所指位置是目标位置。此时计算出 m i d = l o w mid=low mid=low,不满足,执行 l o w = m i d + 1 = h i g h low = mid+1=high low=mid+1=high,循环终止,得答案 high。所以不存在 high 越过了第一个位置的情况。

int binarySearch(int low ,int high,int val,int a[]){
	while(low < high){
		int mid = (high - low) / 2 + low;            避免溢出 !!
		if(a[mid] <= val)	low = mid  + 1;
		else  high = mid;			
	}
	return left;										// 没找到特定值,则返回
}
典例 有效三角形个数

有效三角形个数
题目大意:给出一个序列,求能构成三角形的三元组。
分析:首先对随机序列排序,并令 a ≤ b ≤ c a\leq b\leq c abc。设两个指针 i ,j 分别遍历 a , b。
由于三角形需要满足 a + c > b ; b + c > a ; a + b > c a+c>b;b+c>a;a+b>c a+c>b;b+c>a;a+b>c,根据规定前两项一定满足,则只要找到满足 a + b > c a+b>c a+b>c的项数即可。则可利用 upper_bound 函数找到第一个 >a+b 的位置,即第一个不满足条件的位置。

代码:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int ans = 0;
          sort(nums.begin(),nums.end())  ;
          for(int i = 0;i < nums.size();++i){
                for(int j = i+1;j < nums.size();++j){
                    int right = lower_bound(nums.begin()+j+1,nums.end(),nums[i]+nums[j]) - nums.begin();
                    ans += right - j - 1;       
                }
          }
          return ans;
    }
};
典例 三足鼎立

链接
题目大意:给出一个序列和一条定边长,求出能构成三角形的三元组个数。
分析:由于固定了一条边,那么无法人为规定 a ≤ b ≤ c a\leq b\leq c abc了。需要根据

  1. c < a+b => upper_bound() - 1
  2. c > abs(a-b) => lower_bound()

两条构成三角形的性质来选择。
代码:

int n,p;
int a[MAXN];
int main() {

	scanf("%d%d",&n,&p);
	for(int i = 0; i < n; ++i)	scanf("%d",&a[i]);
	sort(a,a+n);
	int ans = 0;
	for(int i = 0; i < n-1; ++i)	{
		int right = upper_bound(a+i+1,a+n,p+a[i])-a ;
		int left = lower_bound(a+i+1,a+n,abs(p-a[i]))-a;
		ans += right - left;
	}
	printf("%d\n",ans);

	return 0;
}

5.to_string / stoi 数字字符串转换

int stoi(string str)
double stod(string str)
long stol(string str)

to_string(int)
to_string(long)
to_string(double)

string& insert (size_t pos, const string& str);
string& insert (size_t pos, const char* s);
string& insert (size_t pos, size_t n, char c);

int isupper(int c) int toupper(int )
int lower(int c) int tolower(int )
int isalpha(int c ) = isupper || islower
int isdigit(int c)
int isalnum = isalpha || isdigit

典例 A1069 The Black Hole of Numbers - 字符串

题目大意:字符串升序-降序,重复会得到6174,输出这个过程。
注意:输入不是 4 位数字,需要字符串补零,而不能直接拿整数转字符串。
例如:输入 18,得 0081 - 0018 ,是错的;应为 8100 - 0018.

代码:

#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#include<vector>
#include<map>
using namespace std;


string str,a,b;
int main() {

	cin >> str;
	str.insert(0,4-str.size(),'0');
	string tmp;
	while(tmp != "6174") {
		sort(str.begin(),str.end(),greater<char>());
		a = str;
		sort(str.begin(),str.end());
		b = str;
		if(a == b) {
			cout << a << " - " << b << " = 0000" << endl;
			return 0;
		}
		int c = stoi(a) - stoi(b);
		tmp = to_string(c);
		tmp.insert(0,4-tmp.size(),'0');
		cout << a << " - " << b << " = "<< tmp << endl;
		str = tmp;
	}

	return 0;
}

6.string.find/ 返回下标

string.find(st,ed,char )
string.find_first_of()
string.find_first_not_of()
string.find_last_of()
string.find_last_not_of()

均返回的是 int 型的数组下标!

7.make_heap 建堆

将序列排列成二叉堆的形式:

make_heap(_RAIter,_RAIter,_Compare) 默认是大顶堆, less<int>()

弹出堆顶元素放至末尾,并重新调整堆。(即一次堆排序过程)

注意所有的参数必须与 make_heap 保持一致!!,其本质是排成堆的序列而已,所以要指明堆的类型。

pop_heap(_RAIter,_RAIter,_Compare) 

向容器末尾加入一个元素,将其插入到堆的合适位置。(即一次upAdjust)。
注意是末尾,且只能一个元素。

push_heap(_RAIter,_RAIter,_Compare) 

六、阅读理解

典例 A1140 -英文阅读

1140 Look-and-say Sequence (20 分)
Look-and-say sequence is a sequence of integers as the following:

D, D1, D111, D113, D11231, D112213111, …

where D is in [0, 9] except 1. The (n+1)st number is a kind of description of the nth number. For example, the 2nd number means that there is one D in the 1st number, and hence it is D1; the 2nd number consists of one D (corresponding to D1) and one 1 (corresponding to 11), therefore the 3rd number is D111; or since the 4th number is D113, it consists of one D, two 1’s, and one 3, so the next number must be D11231. This definition works for D = 1 as well. Now you are supposed to calculate the Nth number in a look-and-say sequence of a given digit D.

Input Specification:
Each input file contains one test case, which gives D (in [0, 9]) and a positive integer N (≤ 40), separated by a space.

Output Specification:
Print in a line the Nth number in a look-and-say sequence of D.

Sample Input:
1 8
Sample Output:
1123123111

分析:用后一个序列表示出前一个序列,比如 1112213 = 1(3)2(2)1(1)3(1).
代码:

#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;


int d,n;
string s1;
int main() {

	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin>>d>>n;
	s1+=to_string(d);

	while(--n) {
		string s2;
		int j;
		int f = 0;			// 开始的标记
		for(int i = 0; i < s1.size(); i = j) {

			// 找到下一个不同的地方
			for(j = i; j < s1.size(); ++j) {
				if(s1[j] != s1[i])	break;
			}
			s2 += s1[i] + to_string(j-i) ;
		}
		s1 = s2;
//		cout<<"s1:"<<s1<<endl;
	}

	cout<<s1<<endl;

	return 0;
}

、单词汇总

proper 正确 合适

vertex(vertices)顶点

respectively 个别 分别

indices(index) 指标 索引

shipping 运输

incompatible 不相容

oxidizing 氧化

flammable liquid 易燃液体

combinatorial 组合的

optimization 优化

undirected graph 无向图

distinct 不同

preorder and inorder and postorder traversal 先(根左右)中(左根右)后序(左右根)遍历

quadratic 二次方

probing 探测

increments(increase) 增量

collisions 碰撞,冲突

in case 如果

topological 拓扑结构

property 属性

hence 因此

corresponding 相应,对应

clique 集团

subset 子集

adjacent 相邻

recursively 递归的

properties 属性

subtree 子树

descending order 降序

ascending order 升序

crush on 暗恋

analogously 类似的

rearrange 改编 倒换

appear 出现

diagonal 对角线

infix expression 中缀表达式

assume 假设,承担

stuck 卡住

partition 划分

disjoint 不相交

denote 表示

consecutive 连续的

invert 倒置

gang 团伙

threshold 阀值

trigger 触发

、编译器

dev 换用 c++11 标准

在这里插入图片描述
在这里插入图片描述

浮点错误

浮点错误的两种情况:

  • 除以 0 或 取余 0
  • 溢出
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值