PAT:知识点总结

1 数学篇

1.1 最大公约数

  • 辗转相除法
int gcd(int a, int b)
{
   
	return b == 0 ? a : gcd(b, a % b);
}
  • 示例:gcd(4, 6) = 2;

1.1 最小公倍数

a,b的最小公倍数 = a * b / gcd(a, b); ,但 a * b 可能超范围故用:a / gcd(a, b) * b

int lcm(int a, int b)
{
   
	return a / gcd(a, b) * b;
}
  • 示例: lcm(3, 4) = 12;

1.2 素数

  • TIPS 1 :

素数:只能整除1和它本身的数(只有1、本身两个正因子)

边界条件: 0 ,1

  • 定义法:isPrime()

对一个数n:若它能整数:2~sqrt(n)之间的任一数,就是合数,反之为素数

bool isPrime(int n){
   
	if(n <= 1) return false;
	int sqr = sqrt(1.0*n);
	for(int i = 2; i <= sqr; ++i){
   
		if(n % i == 0) return false;
	} 
	return true;
}
void FindPrime(int n)
{
   
	for(int i = 1; i <= n; ++i){
   
		if(isPrime(i) == true){
   
			prime.push_back(i);
			p[i] = true;
		}
	}
}
  • Eratoshenes筛法:GetPrime()

从2开始(10 既不是素数也不是合数),X掉(置为 true )所有素数的倍数(合数),剩下的数就是素数:

bool p[101];    //素数标记表要开打点防止越界产生 段错误
vector<int> prime;
void getPrime(int n){
   
	for(int i = 2; i <= n; ++i){
   
		if(p[i] == false){
   
			prime.push_back(i);
			for(int j = i + i; j <= n; j += i){
   
				p[j] = true;
			}
		}
	}
}

100以内的素数表:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

1.3 分解质因子

TIPS1:

规则:一个数n不可能存在2个 大于 srqt(n)的因子

步骤:
step 1:GetPrime(sqrt(n));
step 2: 枚举这些prime,如果是因子(if(n % prime[i] == 0))求出该质因子个数:while(n % prime[i]) n /= prime[i];
step 3: 循环结束后若 n != 1 ,说明有比sqrt(n)大的质因子,加入fac

struct Fac
{
   
	int f, cnt;
}fac[20];
GetPrime((int)sqrt(n));
for(int i = 0; i < prime.size(); ++i)
{
   
	int now = prime[i];
	if(n % now == 0)
	{
   
		fac[idex].f = now;
		while(n % now == 0)
		{
   
			fac[idex].cnt++;
			n /= now;
		}
		idex++;
	}
}
if(n != 1)
{
   
	fac[idex].f = n;
	fac[idex++].cnt = 1;
}

1.4 分数四则运算

  • TIPS1:

化简规则: 分母:始终为正,分子为0时取1; 约分除gcd
输出规则:分数分为 整数, 假分数, 真分数

  • TIPS2:

乘法可能会超范围,使用long long 存分子分母

  • 化简
int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b); }
Frac Reduce(Frac ans)
{
   
    if(ans.down < 0)
    {
   
        ans.up = -ans.up;
        ans.down = -ans.down;
    }
    if(ans.up == 0)
    {
   
        ans.down = 1;
    }else
    {
   
        int d = gcd(abs(ans.up), ans.down);
        ans.up /= d;
        ans.down /= d;
    }
    return ans;
}
  • 输出:
void ShowAns(Frac ans)
{
   
    ans = Reduce(ans);
    if(ans.down == 1)
    {
   
        printf("%d", ans.up);
    }else if(abs(ans.up) > ans.down)
    {
   
        printf("%d %d/%d", ans.up / ans.down, abs(ans.up) % ans.down, ans.down);
    }else
    {
   
        printf("%d/%d", ans.up, ans.down);
    }
}
  • 补充:

对于除数非法:补充一个标记位:

struct Frac
{
   
    ll up, down;
    bool inf;
    Frac() {
    inf = false; }
};
Frac Divid(Frac a, Frac b)
{
   
    Frac ans;
    if(b.up == 0) ans.inf = true;
    ans.up = a.up * b.down;
    ans.down = a.down * b.up;
    return Reduce(ans);
}
  • 完整版:ShowAns();
void ShowAns(Frac a)
{
   
    a = Reduce(a);
    if(a.inf) printf("Inf");
    else
    {
   
        if(a.up < 0) printf("(");
        ...
        if(a.up < 0) printf(")");
    }
}

1.5 大整数四则运算

一、用结构体bign实现:
  • 存储方式
const int maxn = 1010;
struct Bign
{
   
    int num[maxn];
    int len;
    Bign()
    {
   
        len = 0;
        memset(num, 0, sizeof(num));
    }
};
  • 输入
Bign Change(string n)
{
   
    Bign ans;
    ans.len = n.size();
    for(int i = 0; i < n.size(); ++i)
    {
   
        ans.num[n.size()-1-i] = n[i] - '0';
    }
    return ans;
}
  • 比较
int BignCmp(Bign a, Bign b)
{
   
    if(a.len > b.len) return 1;
    else if(a.len < b.len) return -1;
    else
    {
   
        for(int i = a.len - 1; i >= 0; --i)
        {
   
            if(a.num[i] > b.num[i]) return 1;
            else if(a.num[i] < b.num[i]) return -1;
        }
        return 0;
    }
}
  • 输出:
void ShowBign(Bign a)
{
   
    for(int i = a.len-1; i >= 0; --i) printf("%d", a.num[i]);
}
  • 加法:
Bign Add(Bign a, Bign b)
{
   
    Bign ans;
    int carry = 0;
    for(int i = 0; i < a.len || i < b.len; ++i)
    {
   
        int tmp = a.num[i] + b.num[i] + carry;
        ans.num[ans.len++] = tmp % 10;
        carry = tmp / 10;
    }
    if(carry != 0) ans.num[ans.len++] = carry;
    return ans;
}
  • 乘法:
Bign Prod(Bign a, int b)
{
   
    Bign ans;
    int carry = 0;
    for(int i = 0; i < a.len; ++i)
    {
   
        int tmp = a.num[i] * b + carry;
        ans.num[ans.len++] = tmp % 10;
        carry = tmp / 10;
    }
    while(carry)
    {
   
        ans.num[ans.len++] = carry % 10;
        carry /= 10;
    }
    return ans;
}
  • 减法:
Bign Minus(Bign a, Bign b)
{
   
    if(BignCmp(a, b) < 0)
    {
   
        printf("-");
        swap(a, b);
    }
    Bign ans;
    for(int i = 0; i < a.len; ++i)
    {
   
        if(a.num[i] < b.num[i])
        {
   
            a.num[i+1]--;
            a.num[i] += 10;
        }
        ans.num[ans.len++] = a.num[i] - b.num[i];
    }
    while(ans.len - 1 >= 1 && ans.num[ans.len-1] == 0) ans.len--;
    return ans;
}
  • 除法:
Bign Divid(Bign a, int b, int & r)
{
   
    Bign ans;
    ans.len = a.len;    //对齐
    for(int i = a.len - 1; i >= 0; --i)
    {
   
        r = r * 10 + b;
        if(r < b)   //不够除商0
        {
   
            ans.num[i] = 0;
        }else   //够除商余数除dig
        {
   
            ans.num[i] = r / b;
            r = r % b;  //得到新的余数
        }
    }
    while(ans.len - 1 >= 1 && ans.num[ans.len - 1] == 0) ans.len--;
    return ans;
}
二、用string存储

减法不好做:因为借位可能会使0 -> -1,char没法表示-1;

  • 对齐:
void Align(string & a, string & b)
{
   
    while(a.size() < b.size()) a.insert(0, "0");
    while(b.size() < a.size()) b.insert(0, "0");
}
  • 加法:
string Add(string a, string b)
{
   
    Align(a, b);
    string ans;
    int len1 = a.size(), len2 = b.size(), carry = 0;
    for(int i = 0; i < len1 || i < len2; ++i)
    {
   
        int da = a[len1-1-i] - '0', db = b[len2-1-i] - '0';
        int tmp = da + db + carry;
//        ans.insert(0, to_string(tmp % 10 + '0'));
        ans += (tmp % 10 + '0');
        carry = tmp / 10;
    }
    if(carry)
    {
   
//        ans.insert(0, to_string(carry + '0'));
        ans += (carry + '0');
    }
    reverse(ans.begin(), ans.end());
    return ans;
}
  • 乘法:
string Prod(string a, int b)
{
   
    string ans;
    int len1 = a.size(), carry = 0;
    for(int i = 0; i < len1; ++i)
    {
   
        int da = a[i] - '0';
        int tmp = da * b + carry;
        ans += (tmp % 10 + '0');
        carry = tmp / 10;
    }
    while(carry)
    {
   
        ans += (carry % 10 + '0');
        carry /= 10;
    }
    reverse(ans.begin(), ans.end());
    return ans;
}
  • 除法:
string Divid(string a, int b, int & r)
{
   
    string ans = a;
    for(int i = 0; i < a.size(); ++i)
    {
   
        r = r * 10 + (a[i] - '0');
        if(r < b)
        {
   
            ans[i] = '0';   //注意这里是字符'0',而不是数字0
        }else
        {
   
            ans[i] = (r / b + '0');
            r = r % b;
        }
    }
    while(ans.size() > 1 && ans[0] == '0') ans.erase(0, 1);
    return ans;
}

2. 数据结构篇

2.1 线性表:

TIPS 1 :注意没有有效节点,即空链表,要特殊处理如 PAT 1052

处理思路 1:排序+筛选
  • 思路:

Plan 1. 在原链表上直接排序,要额外用标记flg来筛选出有效节点,同时因为排序后下标不在是初始地址,故应额外用add记录源地址。

struct Node{
   
	int add, data, next;
	bool flg;
}node[maxn];
bool cmp(Node a, Node b){
   
	if(a.flg != b.flg) return a.flg > b.flg;	//true的放前面
	else .... //题目规定排序规则
}

输出:

if(cnt == 0)	//没有有效节点
else{
   
	for(int i = 0; i < cnt; ++i){
   
		if(i < cnt-1) printf("%05d %d %05d", node[i].add, node[i].data, node[i+1].add);
		else printf("%05d %d -1", node[i].add, node[i].data);
	}
}
处理思路 2:下标(地址)排序

Plan 2. 将地址(下标),存入一个vector数组,然后下标排序,排序后格式化输出(推广出一种链表题的通法:按题目规则将链表每个节点地址存入vector中,再格式化输出vector)

struct Node{
   
	int data, nex;
}node[maxn];
bool cmp(int add1, int add2){
   
	node[add1].data ? node[add2].data...	//排序规则
}

输出:

void Print(vector<int> & ans){
   
	if(ans.size() == 0)	//没有有效节点:单独处理
	else{
   
		for(int i = 0; i < ans.size(); ++i){
   
			if(i < ans.size()-1)	printf("%05d %d %05d", ans[i], node[ans[i]].data, ans[i+1]);
			else printf("%05d %d -1", ans[i], node[ans[i]].data);
		}
	}
}

2.2 树:

树的遍历:

DFS(前中后:递归实现 )【注】 有时间学习下非递归实现方法

  • 用vector 记录路径的模板
vector<int> tmp, ans;
void DFS(int id, ...)
{
   
	tmp.push_back(id);
	if(遍历到叶节点)
	{
   	
		//求解,优化各标尺
		tmp.pop_back();		//因为下面return回溯到上一层,所以要"手动"将这层的节点pop出去
		return;
	}
	for()
	{
   
		DFS(nex, ...);
	}
	tmp.pop_back();	//遍历完id的所有后继了,id没用了 将pop其出去
}

BFS(层序:队列实现)

TIPS:
STL中的queue,push操作只是push进去一个副本
对原变量的修改不改变队列中的副本
若要修改队列中元素,应push进去元素的编号(如数组下标、变量的地址,即指针),而不是元素本身

  • 静态树版:
void BFS(int r)
{
   
    queue<int> q;
    q.push(r);
    while(!q.empty())
    {
   
        int now = q.front();
        /*访问当前节点*/
        q.pop();
        /*子节点入队*/
        if(!q.empty()) printf(" "); //可以控制输出空格
    }
}
  • 普通树版:
void BFS(node* r)
{
   
    queue<node*> q;
    q.push(r);
    while(!q.empty())
    {
   
        node* now = q.front();
        /*访问当前节点*/
        q.pop();
        /*子节点入队*/
        if(!q.empty
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值