PAT甲级刷题笔记

PAT甲级刷题笔记

  • STL
    • vector
    • string
    • cctype
  • set
  • 一些自带函数的用法
    • memset
    • round
    • getline()
    • cstring
    • swap
  • 1003 Emergency
  • 1005 Spell It Right
  • 1007 Maximum Subsequence Sum
  • 1009 Product of Polynomials
  • 1010 Radix
  • 1012 The Best Rank
  • 1013 Battle Over Cities
  • 1016 Phone Bills
  • 1018 Public Bike Manangement
  • 1019 General Palindromic Number
  • 1020 Tree Traversals
  • 1021 Deepest Root
  • 1022 Digital Library
  • 1023 Have Fun with Numbers
  • 1028 List Sorting
  • 1029 Median
  • 1031 Hello World for U
  • 1032 Sharing
  • 1038 Recover the Smallest Number
  • 1042 Shuffling Machine
  • 1043 Is It a Binary Search Tree
  • 1047 Student List Course
  • 1051 Pop Sequence
  • 1052 Linked List Sorting
  • 1055 The World's Richest
  • 1056 Mice and Rice
  • 1057 Stack
  • 1059 Prime Factors
  • 1060 Are They Equal
  • 1063 Set Similarity
  • 1064 Complete Binary Search Tree
  • 1065 A + B and C (64bit)
  • 1078 Hashing
  • 1079 Total Sales of Supply Chain
  • 1081 Rational Sum
  • 1089 Insert or Merge
  • 1090 Highest Price in Supply Chain
  • 1093 Count PAT's
  • 1096 Consecutive Factors
  • 1098 Insertion or Heap Sort
  • 1101 Quick Sort
  • 1107 Social Clusters
  • 1111 Online Map
  • 1126 Eulerian Path
  • 1129 Recommendation System
  • 1130 Infix Expression
  • 1131 Subway Map
  • 1139 First Contact
  • 1140 Look-and-say Sequence
  • 1145 Hashing - Average Search Time
  • 1146 Topological Order
  • 1148 Werewolf - Simple Version

STL

vector

vector<int> a(10);
vector<int> b(10,1);//设定vector中的初始值
for(int i = 0;i<10;++i)a[i] = i;
a.erase(0,1);//从第一位开始删除1位
a.erase(a.begin());//同样也是删除第一位
a.clear();//清除a中的数据
a.empty();//空返回true,非空返回false

string

int n = 5;
string a = to_string(n);
string b = "12345";
string c(b,0,3);//后面这个3代表的是个数
string d = b.substr(0,3);
//将数值类型转化为string类
//头文件cstring
int A = stoi(b);
float B = stof(b);
//scanf输入string
string s(30, '\0');    //字符串预先开辟30个char,全部初始化为\0
scanf("%s", &s[0]);    //这样就可以读取string了,但是长度不能超过上面初始化的30
//printf输出string:
printf("%s", s.c_str());

cctype

isalnum()  //如果参数是字母数字,即字母或者数字,函数返回true
isalpha()  //如果参数是字母,函数返回true
iscntrl()  //如果参数是控制字符,函数返回true
isdigit()  //如果参数是数字(0-9),函数返回true
isgraph()  //如果参数是除空格之外的打印字符,函数返回true
islower()  //如果参数是小写字母,函数返回true
isprint()  //如果参数是打印字符(包括空格),函数返回true
ispunct()  //如果参数是标点符号,函数返回true
isspace()  //如果参数是标准空白字符,如空格、换行符、水平或垂直制表符,函数返回true
isupper()  //如果参数是大写字母,函数返回true
isxdigit() //如果参数是十六进制数字,即0-9、a-f、A-F,函数返回true

tolower()  //如果参数是大写字符,返回其小写,否则返回该参数
toupper()  //如果参数是小写字符,返回其大写,否则返回该参数

set

struct node {
int a,b;
}
set<int> s;
for(auto it = s.begin();it!=s.end();++it){
    printf("%d",*it);
    printf("%d",it->key);//如果里面存放的node的话
}
auto it = s.find(1);
s.insert(5);

set<node> s1;
auto it = s1.find(node{1,2});
if(it!=s.end())s.erase(it);//end里面是个标志位,指向end代表没找到

一些自带函数的用法

memset

头文件:cstring

int a[10];
float b[10];
memset(a,1,sizeof(a));
memset(b,1.1,sizeof(b));
//将数组a内的元素初值设置为1

round

头文件:cmath

float a = 2.3, b = -3.8;
printf("%d",round(a),rounf(b));
//输出值为2,-4

getline()

头文件:string
用getline()的时候前面的非string类型数据要换行,防止将回车也读入进去
只需要在最前面换,之后不用,getline会把最后的回车也读入进去

string a;
getline(cin,a);
char a[1000];
cin.getline(a,1000);//char类型使用getline输入

cstring

char a[10];
scanf("%s",&a);
int x = strlen(a);//获取字符串长度
int y = strcmp(a,"abc");// 比较两个字符串大小,左边小返回-1,一样大返回0,右边小返回1

swap

头文件:std::

int a[5] = {1,2,3,4,5};
swap(a[0],a[4]);

leafnode:叶节点,即没有子节点的节点
non-leaf node:非叶节点,有子节点的节点
树的递归遍历:
注意:
有时候传进来的参数只是树里面数据的位置编号,不是节点中的数据大小,输出的时候要再进行转化
在寻找根节点在中序遍历中的位置时要注意约束变量的范围防止溢出

void postorder(int root, int l, int r) {
	//这里面所有的数字都是数组的编号,代表数组的位置,和节点的大小无关
	if(l > r) return;
	int index = l;
	while (index < r && pre[root] != in[index])index++;//不能加到范围之外,到r要终止
	postorder(root + 1, l, index - 1);
	postorder(root + 1 + index - l, index + 1, r);
	post.push_back(root);
}

当图的两个地名给的比较麻烦时,可以采用两个map相互转化的方式进行存储,转编号的顺序为读入的顺序

map<string, int> toi;
map<int, string> tos;
cin >> temps >> temph;
toi[temps] = cnt++;
tos[cnt - 1] = temps;
happy[cnt - 1] = temph;

当数据量过大的时候,临接矩阵无法申请这么大的内存可以采用两种方式,一种是dfs遍历,另一种是使用map[a+10000*b]的方式进行存储。

1003 Emergency

紧急事件

https://blog.csdn.net/lbperfect123/article/details/84281300

最短路径一般有两种写法,一种是Dijkstra,一种是递归dfs
递归时间和 Dijkstra 比慢很多(103ms/3ms)
如果能用贪心算法还是直接用贪心算法比较好
dis为距离,total为总人数,num为最短路径的数目

递归写法,判断权重直接放在函数变量里面进行传递

void dfs(int root, int dis, int total) {
	if (root == c2) {
		if (dis < mindis) {
			mindis = dis;
			maxtotal = total;
			num = 1;
		}
		else if (dis == mindis) {
			num++;
			if (total > maxtotal) maxtotal = total;
		}
		return;
	}
	for (int i = 0; i < n; ++i) {
		if (!visit[i] && arr[root][i] != 0) {
			visit[i] = true;
			dfs(i, dis + arr[root][i], total + rescue[i]);
			visit[i] = false;
		}
	}
}

Dijkstra贪心算法,记得设置初值和填充数组,当有多次循环的时候visit要多次填充

    fill(e[0], e[0] + 510 * 510, inf);
    fill(dis, dis + 510, inf);
    int a, b, c;
	for (int i = 0; i < m; ++i) {
		scanf("%d%d%d", &a, &b, &c);
		e[a][b] = e[b][a] = c;
	}
	dis[c1] = 0;
	tempw[c1] = weight[c1];
	num[c1] = 1;
	for (int i = 0; i < n; ++i) {
		int u = -1, minn = inf;
		for (int j = 0; j < n; ++j) {
			if (visit[j] == false && dis[j] < minn) {
				u = j;
				minn = dis[j];
			}
		}
		if (u == -1)break;
		visit[u] = true;
		for (int v = 0; v < n; ++v) {
			if (visit[v] == false && e[u][v] != inf) {
				if (dis[u] + e[u][v] < dis[v]) {
					dis[v] = dis[u] + e[u][v];
					num[v] = num[u];
					tempw[v] = tempw[u] + weight[v];
				}
				else if (dis[u] + e[u][v] == dis[v]) {
					num[v] = num[v] + num[u];
					if (tempw[u] + weight[v] > tempw[v])
						tempw[v] = tempw[u] + weight[v];
				}
			}
		}
	}

1005 Spell It Right

对于string和char计算的时候不要忘记减掉开头的字符

char a[3] = {'0','a','A'};
int i = a[0] - '0'; 
int j = a[1] - 'a'; 
int k = a[2] - 'A';

1007 Maximum Subsequence Sum

一串数列求和最大的子序列(动态规划)

Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:
10 1 4

算法为从第一位开始往后累加,当当前序列和小于0时,舍弃当前序列,从下一位开始计算,刷新起始点;当当前和比最大值大时,使最大值等于当前和,刷新结束点。即可求得最大和序列。

	for (int i = 0; i < K; ++i) {
		temp += a[i];
		if (temp < 0) {
			temp = 0;
			starttemp = i + 1;
		}
		else if (temp > max) {
			max = temp;
			start = starttemp;
			end = i;
		}
	}

1009 Product of Polynomials

多项式相乘

			//不能这样写的原因在于:
			//有可能两相相加使得系数变为0,这一项就不在计算了不能算在num里面
			if (!visit[m]) {
				visit[m] = true;
				num++;

应该这样写(例如14 系数为-1,22系数为1,这样相加为0以后就不能计算在内了)

for (int i = 2000; i >= 0; i--)
		if (pd[i] != 0.0) num++;

1010 Radix

进制
数据特别大的时候需要用到longlong.
int和long一般是-232~232-1(-2,147,483,648 2,147,483,647) (2×10^10)
longlong 是(- 9223372036854775808 ~ 9223372036854775807 (9×10^19)
更大的数就要用字符串进行处理了。
这个题用目标数的进制或者目标数本身作为结束点 详见1010
字符串的复制:

第一种情况:
char* p=“how are you ?”;
char name[20]=“ABCDEFGHIJKLMNOPQRS”;
strcpy(name,p); //name改变为"how are you ? OPQRS " ====>错误!
strncpy(name,p,sizeof(name)) //name改变为"how are you ? " ====>正确!
第二种情况:
char* p=“how are you ?”;
char name[20];
strcpy(name,p); //name改变为"how are you ? 未知字符 " ====>错误!
name[sizeof(name)-1]=’/0’ //和上一步组合,得到正确的结果!
strncpy(name,p,sizeof(name)); //name改变为"how are you ? " ====>正确!
第三种情况:
char* p=“how are you ?”;
char name[10];
strcpy(name,p); //name改变为"how are yo" ====>无结束符’/0’,错误!
name[sizeof(name)-1]=’/0’ //和上一步组合,弥补结果。但要注意,字符传递错误!
strncpy(name,p,sizeof(name)); //和单纯的一步strcpy结果一样!

简单来说采用strncmp比较好,三个参数依次要复制去的字符串,第二个为源字符串,第三个为要复制去的字符串的大小

1012 The Best Rank

四舍五入的小技巧:在后面加上0.5即可
或者使用自带的round函数。

1013 Battle Over Cities

最大联通分量计算,简单的dfs遍历即可

1016 Phone Bills

字符串定义的时候一定要多留一位,不然各种地方都会出错
本题没有账单的客人不用进行打印(但是题目没说)

1018 Public Bike Manangement

Dijkstra + DFS
如果只有Dijkstra是不可以的,因为minNeed和minBack在路径上的传递不满足最优子结构,不是简单的相加的过程
只有在所有路径都确定了之后才能区选择最小的need和最小的back
在路径存储的时候只能将前面的节点存储在后面节点的数组;里(即遍历的时候是倒着的)反过来的过无法正确的根据条件更新路径

1019 General Palindromic Number

palindromic 回文数
symmetric 对称(1040)

1020 Tree Traversals

树的遍历
preorder 先序
inorder 中序
postorder 后序
levelorder 层序
一般是知道中序先序转后序或者知道中序后序转先序
知道先序后序转中序有可能不唯一,方法和之前一样

1021 Deepest Root

注意gethight函数写法,和AVL、红黑树中的写法类似

int gethigh(int root) {
	visit[root] = true;
	int temphigh = 0;
	for (int i = 0; i < v[root].size(); ++i) {
		if (!visit[v[root][i]]) 
			temphigh = max(temphigh, gethigh(v[root][i]));
	}
	return temphigh + 1;
}

1022 Digital Library

连续读入遇到回车结束读入的方法
首先,getchar()函数的作用是读入下一个字符(不管是空格还是回车都可以读入)
当需要连回车一起读入的时候一般使用**getline()函数,但是如果之前有数字输出,需要使用一个getchar()**函数把回车读掉,不然会出现错误

scanf("%d", &tempid);
getchar();
string tempbooktitle, tempauthor, tempkeywords, temppublisher, tempyear;
getline(cin, tempbooktitle);

连续读入遇到回车停止的方法之一也使用**gerchar()**函数
这样使用的前提是字符串之间有空格,不然getchar()会读到之后需要使用的内容

while (cin >> tempkeywords) {
	m[tempkeywords].push_back(tempid);
	char c = getchar();
	if (c == '\n')break;
}

1023 Have Fun with Numbers

使用字符串进行进位运算,只需要增加一个标志位,当大于10的时候使标志位等于1,加完以后把他清0。

temp = 2 * temp + decimal;
decimal = 0;
if (temp >= 10) {
	temp %= 10;
	decimal = 1;
}

在使用字符串进行计算的时候要考虑两个问题,第一是遍历的时候判断位要使用len+1,因为要把字符串的结束位也算进去。第二是多次循环的时候要更新字符串的长度len(因为使用 len = strlen(a) 获取的长度是不会变的)

1028 List Sorting

本题在c不同的情况下需要多个cmp函数
可以直接将c变为全局变量,然后在cmp函数中分情况

struct NODE {
    int no, score;
    char name[10];
}
int c;
int cmp1(NODE a, NODE b) {
    if(c == 1) {
        return a.no < b.no;
    } else if(c == 2) {
        if(strcmp(a.name, b.name) == 0) return a.no < b.no;
        return strcmp(a.name, b.name) <= 0;
    } else {
        if(a.score == b.score) return a.no < b.no;
        return a.score <= b.score;
    }
}

1029 Median

判断语句中一定要加上个数的约束条件,而且要放在前面
本题方法的前提是这是两个递增的数字序列
本题开始就给定了n1和n2因此中位数的位置是固定的,只需要使用count从0开始判断是否到了中间位置即可,到了中间位置就进行输出,如果第二个数组读完了还是没到中间位置,代表第二个数组的数太少太小,接着第一个数组往后读就可以了
简单的题不要使用vector,时间会大很多(大概一倍)

使用循环的时候如果使用数组里面的值作为终止条件,那么一定要加上数组的编号不i超过数组边界的判断条件。

1031 Hello World for U

本题寻找u序列的方法为从两边的对称结构入手,先将总个数加上2(补齐和底边的重合部分)然后三等分向下取整就为单个对称结构的数目

n1 = (len + 2) / 3 - 1;
n2 = len - 2 * n1;

1032 Sharing

注意%s和%c的使用区别
对于单个的字符来说,这两者似乎都是一样的功能,但是要注意的是使用%c的时候要把读入数据的space也打出来,不然%c会把空格读进去而不是读入单个的字符,%s没有这个问题,%s读入的时候会自动过滤掉space

1038 Recover the Smallest Number

string直接加减是直接把string前后拼接在一起,这个可以直接应用到cmp函数中
两两组合更小的放在前面

bool cmp(string a, string b) {
	return a + b < b + a;
}

排序的时候可以直接在此基础上进行排序
Sample Input
5 32 321 3214 0229 87

int n;
cin >> n;
string * a = new string[n];
for (int i = 0; i < n; ++i) cin >> a[i];
sort(a, a + n, cmp);

这样就可以使组合起来更小的放在前面,整个序列组合起来也是所有排列组合中最小的

1042 Shuffling Machine

定义的char类型读取字符串的时候需要多定义一个单位,但是直接定位内部字符的时候不需要

char a[5] = { 'S', 'H', 'C', 'D', 'J' };

一般对于数据得到处理当正向很麻烦时候,考虑反向处理
本题如果用中间函数将start的值传递给end的话会很麻烦,因此倒过来,end通过中间函数去获取start的值

for (int i = 0; i < k; ++i) {
		for (int j = 1; j < 55; ++j) 
			end[next[j]] = start[j];
		for (int j = 1; j < 55; ++j) 
			start[j] = end[j];
	}

1043 Is It a Binary Search Tree

在进行递归的时候用一增一减来约束保证这个树是BST或者mirrorBST。

int i = root + 1, j = end;
while (!flag && i <= end && a[i] < a[root])i++;
while (!flag && j > root && a[root] <= a[j])j--;
while (flag && i <= end && a[i] >= a[root])i++;
while (flag && j > root && a[root] > a[j])j--;
if (i - j != 1)return;

1047 Student List Course

当字符串的数据量过大时,且格式固定时就不再使用string类型,选择使用char读取,防止超时。(记得多定义一位)

1051 Pop Sequence

按照srack的工作方式进行操作
先将序列记录下来
然后按照正常的顺序往stack里push,当top的值和序列当前值相等时pop,并序列当前值后移,stack的大小大于给定值时,结束。
最后看序列当前值的序号是否等于n+1,是的话输出yes,否则输出no

1052 Linked List Sorting

链表相关的题首先要输入数据然后进行一遍遍历,剔除掉不在链表中的数据。
其次链表的地址一般都带有格式,需要再输出时候定义格式。
且题目如果未说明的话有可能链表全部未连接。
要考虑只有一个节点的特殊情况。

1055 The World’s Richest

当数据量太大时(超过100000)可以进行分类整理,选择性的剔除掉部分数据,只保存前多少个数据即可

1056 Mice and Rice

本题有个小技巧,就是如果分了num组,就相当于有num个人被提取出来,剩下的人那就是num+1名
所以本题的例子排名没有4,因为有两个第三名

1057 Stack

树状数组(Binary Indexed Tree, BIT)

https://www.cnblogs.com/xenny/p/9739600.html

lowbit(x)=2^p
其中:P是指将x转化为二进制之后从右往左数第一个一的位置。
详细见图

https://blog.csdn.net/DK714/article/details/81192727

lowbit(x)也可以理解为能整除x的最大的2的幂次
例如lowbit(7) = 1, lowbit(6) = 2, lowbit(8) = 0

https://www.liuchuo.net/archives/2268

构建一个树状数组

int c[maxn];
void update(int x, int k) {//c[i]里面存放的是树状数组结构里面的a[i]的和
//1存放1  2存放1,2  3 存放3  4存放1,2,3,4
	for (int i = x; i < maxn; i += lowbit(i))
		c[i] += k;
}

int getsum(int x) {//根据树状数组结构里面的数据个数计算前i项的和
	int sum = 0;
	for (int i = x; i >= 1; i -= lowbit(i))
		sum += c[i];
	return sum;
}

中位数的题一般根据前面数的个数进行处理

1059 Prime Factors

首先再longint的范围内判断500000以内的素数足够了,因为如果是500000以上的两个素数相乘就已经超过了longint的范围。
在小数据量上面简单的进行单个素数判断比较快,但是特别大的素数就需要迭代很久,但是用素数表都可以很快的解决问题。

vector<int> prime(500000, 1);
for (int i = 2; i*i < 500000; ++i) {//建立素数表
		for (int j = i; i*j < 500000; ++j) {
			prime[i*j] = 0;
		}
	}

1060 Are They Equal

string中单个的字符可以直接用%c在scanf中输出,整体的才需要转c_str
考虑全部为0的特殊情况

if (a[0] == '0')cnt1 = 0;//如果小数位全是0把扣掉的次方补回来,防止后面不相等
/*
6 0 00.00
NO 0.000000*10^0 0.000000*10^-2
*/

1063 Set Similarity

想要输出%需要使用%%

int a = 1;
printf("%d%%,a);//1%

1064 Complete Binary Search Tree

对于BST来说,只需要前序遍历即可确立二叉树
如果BST的数据太多,超过32层,int的范围就无法满足了,这个时候就需要再加上层数数据,单个编号会溢出

1065 A + B and C (64bit)

因为A、B的大小为[-263, 263],用long long 存储A和B的值,以及他们相加的值sum:
如果A > 0, B < 0 或者 A < 0, B > 0,sum是不可能溢出的
如果A > 0, B > 0,sum可能会溢出,sum范围理应为(0, 264 – 2],溢出得到的结果应该是[-263, -2]是个负数,所以sum <= 0时候说明溢出了
如果A < 0, B < 0,sum可能会溢出,同理,sum溢出后结果是大于0的,所以sum >= 0 说明溢出了

两者溢出是可能溢出到0的

1078 Hashing

哈希表二次探测写法(仅具有正增量)
Quadratic probing (with positive increments only)

void insert(int key, int num) {
	for (int step = 0; step < num; ++step) {
		int index = (key + step * step) % num;
		if (hashtable[index] == 0) {
			hashtable[index] = 1;
			printf("%d", index);
			return;
		}
	}
	printf("-");
}

任何和素数有关的题目都要注意,1不是质数!!!
要考虑这种情况

1079 Total Sales of Supply Chain

当有时候函数用指针时间不够时,可以考虑使用&来进行传参,会快一点

1081 Rational Sum

求最大公约数的函数:

long long gcd(long long a, long long b) {
	return b == 0 ? abs(a) : gcd(b, a%b);//abs绝对值函数
}

当最大范围为int或者long int类型的数据产生计算时就需要使用longlong类型防止数据溢出
分数相乘的算法:

scanf("%lld/%lld", &a, &b);
gcdvalue = gcd(a, b);
a /= gcdvalue;
b /= gcdvalue;
suma = a * sumb + b * suma;
sumb = b * sumb;
gcdvalue = gcd(suma, sumb);
suma /= gcdvalue;
sumb /= gcdvalue;

1089 Insert or Merge

插入排序:首先寻找中间序列乱序开始的地方,然后比较后面乱序的部分是否与开始序列相同的,如果相同的画代表就是插入排序,否则为归并排序(或其他排序)
多添加一位进行sort排序即为下以序列的排序
归并排序:为归并排序以后寻找的方法为从开始序列依次进行归并排序只到与给的中间序列相同为止,然后再进行一次归并排序即可

while (flag) {
		flag = 0;
		for (int i = 0; i < n; ++i) {
			if (a[i] != b[i]) {
				flag = 1;
				break;
			}
		}
		pos *= 2;
		int i;
		for (i = 0; i < n / pos; ++i)sort(a + i * pos, a + i * pos + pos);
		sort(a + i * pos, a + n);
	}

1090 Highest Price in Supply Chain

提示错误信息:count:不明确的符号.
查阅资料找到了答案:命名空间std有一个count函数
编译器不知道调用的是:std::count(),还是用户定义的全局变量count,故报错
解决办法,在count前面加上作用域标识符:::count即可
如果同时要用到std中的count函数,加上命名空间和作用标识符即可:std::count()
或者更换变量名称

1093 Count PAT’s

算法为:先遍历一遍T的数量
然后第二遍遍历的时候遇到P则countp++,遇到T则countt–,遇到A则total+=count*countp。
思想为将三元的数量简化为两元的数量直接相乘,以中间的A作为循环标志累加。

for (int i = 0; i < a.size(); ++i)
	if (a[i] == 'T')countt++;
for (int i = 0; i < a.size(); ++i) {
	if (a[i] == 'P')countp++;
	else if (a[i] == 'T')countt--;
	else total += countt * countp;
}

1096 Consecutive Factors

不用算连续因子最多不会超过12个,也不需要三重循环,两重循环即可,直接去计算当前部分乘积能不能整除N
分析:1、如果只有一个因子,那么这个数只能为1或者质数。因此我们主要去计算两个及以上因数的情况。
2、在有两个及以上的数连乘中,因数的最大上限为sqrt(N) + 1
3、因此思路就是,不断构造连乘,看连乘的积是否是N的因数,如果是,则看这部分连乘的数的个数是否比已记录的多。
4、用变量first记录连乘的第一个数字,这里我把它赋初值为0,如果在寻找N的因数过程中,first没有改变,那么就表明N是1或者是一个质数~

直接从1开始寻找因子序列即可,不用对n进行一步一步的拆分

1098 Insertion or Heap Sort

堆排序:(完全二叉树)
最大堆是当前父节点都大于子节点,否则就要交换。然后把顶堆的最大数放到数列的最后面去。然后再进行下一步操作。
当最大数放到后面去的时候的操作不是插入而是交换!!!

for (flag; flag > 1; --flag) {
		if (b[flag] < b[0]) {
			swap(b[0], b[flag]);
			break;
		}
	}

当二叉树编号的时候采用从0开始的方式比较方便,左子树是2×index+1,右子树是2×index+2。
当查找层数和编号的时候从1开始比较方便,层数从0层开始,每一层为[2level,2level+1)比较方便

1101 Quick Sort

有时候一直出现格式错误的话在最后一行后面加一个回车方便识别(有可能是因为没有结果的话第二行也要显示出来,没有第二行直接判格式错误)
因此有时候遇到第二行可能没有数据输出的情况需要在后面加一个回车

1107 Social Clusters

并查集的标准算法:

int findfather(int x) {
	int a = x;
	while (x != father[x])//查找根节点
		x = father[x];
	while (a != father[a]) {
		//路径优化,把所有的多级子节点根的父全部改为根节点
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

void unionnode(int a, int b) {
	int fa = findfather(a);
	int fb = findfather(b);
	if (fa != fb)father[fb] = fa;
}

其次需要两个容器,一个为存放自己的根节点father,一个为判断是否为根节点的遍历容器isroot

for (int i = 1; i <= n; ++i) 
		if (findfather(i) == i)cnt++;//这里一定要用find(i)不能用father[i]
		//后面有个题需要随时更改根节点,就不能用father,有的地方更新以后没有变更
		//father是自己的点即为根节点,根节点的数量即为并查集的数量
	for (int i = 1; i <= n; ++i) 
		isroot[findfather(i)]++;
		//统计每个根节点里面有多少个节点

1111 Online Map

图的最短路径在使用贪心算法进行查询的时候,多条路径需要多次寻找,不同的第一权重会影响第二权重的累加发生干涉,不能混在一起。

1126 Eulerian Path

even drgree:偶度
od drgree:奇度

1129 Recommendation System

set重载小于号

struct node {
	int value, cnt = 0;
	//重载set<号,记住格式,或者是const node a,const node b
	bool operator < (const node & a) const {
		return (cnt != a.cnt) ? cnt > a.cnt:value < a.value;
	}
};

1130 Infix Expression

在写小于号的时候最好在递归中一步一写,不然多了可能会出来乱掉了。

/*
附程序结果:
+((a*b-(c/(+((d*(+(g-(*(-h-(e/f)))))))))))  我的
+((a*b)-(c/(+((d*(+(g-(*(-h)))))-(e/f)))))
这样的问题在于,仅对于第一级的左右子树进行了括号处理,当子树多的时候括号就乱掉了,不能采用这样的方法,
*/

1131 Subway Map

遇到是编号的题一定要注意输出格式!!!链表之类的也是。
递归写法需要再牢记一下。
什么时候更改visit,什么时候改回visit。
什么时候push什么时候pop

1139 First Contact

注意编号是0的情况(4-digit),这个时候无法分辨正负(如果有含义的话)

1140 Look-and-say Sequence

string类型直接进行累加就可以了,可以直接加在字符串的后面。
复制的时候直接进行等于就可以了,可以顶替掉原来的值,不用清除
char类型的复制可以使用strncpy

1145 Hashing - Average Search Time

hash表的查找步骤和插入的步骤一样,但是查找的时候需要查找到size同大小的数字,并且当hash[temp] = t或者hash[temp] = 0的时候退出 。等于0的时候代表已经没有存储这个数字了。
正常的二次插入步骤就应该查询到size,但是这一步没有意义之前写的时候把它省掉了,但是查询的时候要加上,这一步算时常的。

//哈希表的查找
int ans = 0;
for (int i = 0; i < m; ++i) {
	scanf("%d", &a);
	for (int j = 0; j <= size; ++j) {//这个地方要加上等于 !!!(为什么?)
		ans++;
		int pos = (a + j * j) % size;
		if (h[pos] == a || h[pos] == 0)break;//如果=0表示没有存放此数字也要退出
	}
}

1146 Topological Order

有向图的拓扑序列是判断图有无环的方法之一,标准的方法是使用监测输入点的入度是否为1,我之前想的方法也是正确的,采用判断是否访问过。
错的原因在于我为了减少数据量在第一次监测到访问点之后就进行了跳出,使得后序的数据没有读入完全,影响到了后序点的读入,因此错误。
在后续的做题过程中,如果出现多趟的数据读入,不能随便跳出,要将数据读完。

for (int j = 0; j < n; ++j) {
		scanf("%d", &temp);
		visit[temp] = true;
		for (int x = 0; x < e[temp].size(); ++x) {
			if (visit[e[temp][x]] && !flag) {
				flag = 1;
				out.push_back(i);
				break;
			}
		}
		//if(flag == 1)break;//这里不能break出去,要将数据读完
	}

1148 Werewolf - Simple Version

这个题有点意思
vs里面可以过的代码,pat里面过不了,因为abs输出的不是int类型,不能直接放到int类型数组里面,需要前面转一个(int)。
但是直接定义vector(int)却可以正常运行

int main(void) {
	int n, lie[110];
	scanf("%d", &n);
	vector<int> a(n + 1);
	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			int cnt = 0;
			fill(lie, lie + 110, 1);
			lie[i] = lie[j] = -1;
			for (int x = 1; x <= n; ++x) {
				if (a[x] * lie[abs(a[x])] < 0)cnt++;
			}
			if (cnt == 2 && a[i] * lie[abs(a[i])] * a[j] * lie[abs(a[j])] < 0) {
				printf("%d %d", i, j);
				return 0;
			}
		}
	}
	printf("No Solution"); 
}
//pat中可以过的两种写法
int main(void) {
	int n;
	scanf("%d", &n);
	vector<int> a(n + 1);
	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			int cnt = 0;
			vector<int> lie(n+1,1);
			lie[i] = lie[j] = -1;
			for (int x = 1; x <= n; ++x) {
				if (a[x] * lie[abs(a[x])] < 0)cnt++;
			}
			if (cnt == 2 && a[i] * lie[abs(a[i])] * a[j] * lie[abs(a[j])] < 0) {
				printf("%d %d", i, j);
				return 0;
			}
		}
	}
	printf("No Solution"); 
}

int main(void) {
	int n, lie[110];
	scanf("%d", &n);
	vector<int> a(n + 1);
	for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			int cnt = 0;
			fill(lie, lie + 110, 1);
			lie[i] = lie[j] = -1;
			for (int x = 1; x <= n; ++x) {
				if (a[x] * lie[(int)abs(a[x])] < 0)cnt++;
			}
			if (cnt == 2 && a[i] * lie[(int)abs(a[i])] * a[j] * lie[(int)abs(a[j])] < 0) {
				printf("%d %d", i, j);
				return 0;
			}
		}
	}
	printf("No Solution"); 
}

你可能感兴趣的:(PAT甲级刷题笔记)