判断素数
HDU1059
求一个数字的质因子之积时,只需要考虑小于等于根号n的质因子,但是最后剩下的可能还是个质数,所以要格外考虑下
bool isPrime(int x){
if(x<=1)
return false;
for(int i=2; i<=int(sqrt(x*1.0)); i++){
if(x % i == 0)
return false;
}
return true;
}
最大公约数gcd 辗转相除法
int gcd(long long a, long long b){
return b == 0 ? abs(a) : gcd(b, a%b);
}
最小公倍数
int lcm(int a, int b) {
return a * b / gcd(a, b);
}
四则运算
https://pintia.cn/problem-sets/994805342720868352/problems/994805378443755520
//定义分子分母
struct fraction{
ll up, down;
}a, b;
//gcd最简化
fraction reduction(fraction res){
if(res.down<0){ //保证分母是正数
res.up = -res.up;
res.down = -res.down;
}
if(res.up == 0)
res.down = 1;
else{
int t = gcd(abs(res.up), abs(res.down));
res.up /= t;
res.down /= t;
}
return res;
}
//分数加法
fraction add(fraction f1, fraction f2){
fraction res;
res.up = f1.up * f2.down + f2.up * f1.down;
res.down = f1.down * f2.down;
return reduction(res);
}
//输出形式
void showResult(fraction res){
res = reduction(res);
if(res.up<0)
cout << "(";
if(res.down == 1)
cout << res.up;
else if(abs(res.up) > res.down)
cout << res.up/res.down << " " << abs(res.up)%res.down << "/" << res.down;
else
cout << res.up << "/" << res.down;
if(res.up<0)
cout << ")";
}
将n按照d进制逆序
while(n){
sum = sum * d + n % d;
n = n / d;
}
结构体的定义
struct record{
char name[25];
int month, dd, hh, mm;
bool status;
}rec[maxn], temp;
排序cmp
bool cmp(record a, record b){
int s = strcmp(a.name, b.name);
if(s != 0)
return s < 0; //优先按照姓名字典序从小到大排序
else if(a.month != b.month) //按照月份排序
return a.month < b.month;
else if(a.dd != b.dd)
return a.dd < b.dd;
else if(a.hh != b.hh)
return a.hh < b.hh;
else
return a.mm < b.mm;
}
//比较字符串或者是char数组时,用strcmp
return strcmp(a.id,b.id)<0;
//使用方法
sort(rec, rec+N, cmp);
枚举2^n种情况(熄灯问题)
直到guess()==flase退出枚举
while(guess()){
press[1][1]++;
int k=1;
while(press[1][k] > 1){
press[1][k] = 0;
k++;
press[1][k]++;
}
}
大整数加法
string add(string s1,string s2) // 加
{
int i,j,k,t;
string sum;
t=0;
k=0;
if(s2.size()>s1.size())
{
s1.swap(s2);
k=1; //标记是否s1,s2交换;保证s2长度不大于s1
}
for(i=0;i<s2.size();i++)
{
sum+=(s1[s1.size()-1-i]-'0'+s2[s2.size()-1-i]-'0'+t)%10+'0'; //从末位开始逐位做加法,t表示是否进位
if((s1[s1.size()-1-i]-'0'+s2[s2.size()-1-i]-'0'+t)>=10)
t=1;
else t=0;
}
if(s1.size()==s2.size() && t==1)
sum+='1';
else
{
for(j=s1.size()-1-i;j>=0;j--)
{
sum+=(s1[j]-'0'+t)%10+'0';
if(s1[j]-'0'+t>=10)
t=1;
else t=0;
}
if(t==1)
sum+='1';
}
reverse(sum.begin(),sum.end());
return sum;
}
大整数减法
string sub(string s1,string s2) // 减
{
int flag=0,i;
if(s1.size()<=s2.size())
{
if(s2.size()>s1.size())
flag=1;
else
{
for(i=0;i<s1.size();i++)
{
if(s1[i]>s2[i])
break;
else if(s1[i]<s2[i])
{
flag=1;
break;
}
}
}
}
//保证s2小于等于s1
if(flag==1)
s1.swap(s2);
string ans;
reverse(s1.begin(),s1.end());
reverse(s2.begin(),s2.end());
for(i=0;i<s1.size();i++)
{
if(i>=s2.size())
s2+='0';
ans+=s1[i]-s2[i]+'0';
if(s1[i]<s2[i])
{
ans[i]+=10;
s1[i+1]-=1;
}
}
reverse(ans.begin(),ans.end());
for(i=0;i<ans.size();i++)
{
if(*ans.begin()=='0')ans.erase(ans.begin());
else break;
}
if(flag==1)
ans.insert(0,"-");
return ans.size()==0?"0":ans;
}
大整数乘法
string mul(string s1,string s2) // 乘
{
int i,j,t;
vector<int> num1,num2,ans;
string s;
//倒过来放算的时候比较方便
for(i=s1.size()-1;i>=0;i--)
{
num1.push_back(s1[i]-'0');
}
for(i=s2.size()-1;i>=0;i--)
{
num2.push_back(s2[i]-'0');
}
//按照平常的计算方式计算
for(i=0;i<num2.size();i++)
for(j=0;j<num1.size();j++)
{
if(i+j>=ans.size())
ans.push_back(num1[j]*num2[i]);
else
ans[i+j]+=num1[j]*num2[i];
}
//解决进位问题
t=0;
for(i=0;i<ans.size();i++)
{
ans[i]+=t;
t=ans[i]/10;
ans[i]%=10;
}
if(t!=0)
ans.push_back(t);
reverse(ans.begin(),ans.end());
for(i=0;i<ans.size();i++)
s+=ans[i]+'0';
return s;
}
将字符串转化为double,整数转为字符串
string n;
double x = atof(n.c_str())
//整数转为字符串
int n;
string m = to_string(n);
读入未知长度以空格区分的一行数据
//例如 * + 11.0 12.0 + 24.0 35.0
string temp;
while(cin >> temp){
n[index++] = temp;
char ch = getchar();//通过getchar()来判断最后输入回车符结束
if(ch == '\n')
break;
}
优先队列
队首元素一定是当前队列中优先级最高的那一个
priority_queue<int> q;
priority_queue<int, vector<int>, less<int> > q;
priority_queue<double, vector<double>, greater<double> > q;
//默认最大的最优先,通过改变greater实现数字越小,优先级越高
中缀转为后缀表达式(含!)
string s;
vector<char> sta;
stack<char> temp;
getline(cin, s);
sta.clear();
for(int i=0; i< s.size(); i++){
if(s[i] == ' ')
continue;
if(s[i] == 'V' || s[i] == 'F')
sta.push_back(s[i]);
else{
if(s[i] == '('){
temp.push(s[i]);
}
else if(s[i] == ')'){
char x = temp.top();
sta.push_back(x);
temp.pop();
temp.pop();
}
else if(s[i] == '!'){
temp.push(s[i]);
}
else if(s[i] == '|' || s[i] == '&'){
if(temp.empty())
temp.push(s[i]);
else{
while(temp.top() == '|' || temp.top() == '&' || temp.top() == '!'){
sta.push_back(temp.top());
temp.pop();
if(temp.empty())
break;
}
temp.push(s[i]);
}
}
}
}
for(int i=0; i<temp.size(); i++){
sta.push_back(temp.top());
temp.pop();
}
KMP
用string存储有时会超时,可用char[]数组代替
//将字符串输入到s中,并且增加s[0]=k,输入从s[1]开始
char s[100];
scanf("%s", s+1);
s[0] = 'k';
//求输入到数组中的字符串长度
int len = strlen(s);
//nextval
void cal_nextval(string t){
int i=1, j=0;
nextval[1] = 0;
while(i <= t.length()){
if(j == 0 || t[i] == t[j]){
i++;
j++;
if(t[i] != t[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
} else
j = nextval[j];
}
}
//KMP
int kmp(string s, string t){
cal_nextval(t);
int i=1, j=1;
int c = 0;
while(i <= s.length() && j <= t.length()){
if(j==0 || s[i] == t[j]){
i++;
j++;
} else
j = nextval[j];
if(j>t.length()){
c++;
j=nextval[j];
}
}
return c;
}
kmp next 变形
char s[1005];
char t[1005];
int nextval[1005] = {0};
void get_next(int len_t){
int i=0, j=-1;
nextval[0] = -1;
while(i < len_t){
if(j == -1 || t[i]==t[j]){
i++;j++;
nextval[i] = j;
} else
j = nextval[j];
}
}
int kmp(int len_s, int len_t){
get_next(len_t);
int count=0;
int i=0, j=0;
while(i < len_s){
if(j==-1 || s[i] == t[j]){
i++;j++;
}
else
j=nextval[j];
if(j>=len_t){
j=0;
count++;
}
}
return count;
}
int main(){
while(cin >> s){
if(s[0] == '#' && strlen(s) == 1)
break;
cin >> t;
int len_s = strlen(s);
int len_t = strlen(t);
cout << kmp(len_s, len_t) << endl;
}
}
map的使用
map的遍历顺序与输入顺序是不一致的,内部是红黑树机制
map<string, string> m;
m[b] = a; //赋值或者取值
//map初始化
map<int, string>m = {{0,"ling"}, {1,"yi"}}
//迭代
map<string, string>::iterator iter;
for(iter=m.begin(); iter != m.end(); iter++)
cout << iter->first << " " << iter->second << endl;
//迭代法二
for(auto & it : mp){
nod temp;
temp.cardnum = it.first;
temp.nt = it.second;
v2.push_back(temp);
}
list的使用
list<int> lit[10005];
//插入
lit[id1].push_back(id2);
//合并两个list
lit[id1].merge(lit[id2]);
//去重,unique只能去重相邻元素
lit[id1].unique();
//list不能按下标取值,只能用迭代器
list<int>::iterator it;
int i=0;
for(it=lit[id1].begin();it!=lit[id1].end();++it)
cout << *it << endl;
前序和中序构造二叉链表
struct node{
int data;
node *lc, *rc;
}*N;
node * PreInCreate(int prei, int prej, int ini, int inj){
node *root = new node;
root->data = pre[prei];
// cout << root->data << " ";
int i;
for(i=ini; in[i]!=root->data; i++);
int llen = i-ini;
int rlen = inj-i;
if(llen)
root->lc = PreInCreate(prei+1, prei+llen, ini, i-1);
else
root->lc = NULL;
if(rlen)
root->rc = PreInCreate(prej+i-inj+1, prej, i+1, inj);
else
root->rc = NULL;
return root;
}
node *TT = new node;
TT = PreInCreate(1, k, 1, k);
数组置为inf
#define inf 0x3f3f3f
//int e[1000][1000], dis[1000];
fill(e[0], e[0] + 510 * 510, inf);
fill(dis, dis + 510, inf);
memset(dis, 0, sizeof(dis));
大整数正反相加
如s=1234567
即求 1234567+7654321
Cin Cout 超时
一、使用scanf, printf代替
char name[10];
scanf("%s", name);
//可以直接strcmp(name, "xxxx")==0判断
实在是要用string
!!!
node.name.resize(10);
scanf("%s", &node.name[0]);//这样输入会自动在不足resize的位置以“\0”填充,所以不能直接跟给定的“xxx”进行==比较
printf("%s", node.name.c_str());
//加上.c_str()之后,会把后面的"\0"去掉
!!!
char[]数组和string比较字符串时有区别
char[]
return (strcmp(a.name, b.name) < 0);
string
return a.name < b.name;
vector初始化
vector<int>v(n);
//这样就可以直接给v[i]赋值,如果不初始化(不加(n))只能push_back
关于string
可以用s.substr(1,3)代表子串,从1开始的3位
二、直接cin >> node[i].ch,也可能会超时,先cin到临时变量,再存到node里面去
三、map<string, vector > stu;
👆比👇快
map<string, int> stu;
vector course;
放在
struct stu{
char id[8], name[9];
int score;
}student;
scanf("%s%s%d", student.id, student.name, &student.score);
unordered_map比map快
未在map里的东西,直接用时等于0
图————求两点间的最短距离,最短距离一致则找最小花费 dijskra+dfs模板
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f
//图————求两点间的最短距离,最短距离一致找最小花费
//dijskra+dfs, dijskra解决最短路径,dfs解决花费最少问题
//e代表两点间距离,dis代表start到该点的最短路径,cost代表两点间花费
int e[510][510], dis[510], cost[510][510];
int n, m, s, d;
//pre代表从起点到该点的路径
vector<int> pre[510];
//path代表最终路径,temppath是临时变量
vector<int> path, temppath;
//visit代表该点是否被访问过
bool visit[510];
//mincost代表最小花费
int mincost = inf;
void dfs(int v){
temppath.push_back(v);
//更新路径,找到花费最少的
if(v == s){
int tempcost = 0;
for(int i=temppath.size()-1; i>0; i--){
int id=temppath[i], nextid=temppath[i-1];
tempcost += cost[id][nextid];
}
if(tempcost < mincost){
mincost = tempcost;
path = temppath;
}
//
temppath.pop_back();
return;
}
for(int i=0; i<pre[v].size(); i++)
dfs(pre[v][i]);
temppath.pop_back();
}
int main(){
fill(e[0], e[0]+510*510, inf);
fill(dis, dis+510, inf);
cin >> n >> m >> s >> d;
for(int i=0; i<m; i++){
int a, b;
cin >> a >> b;
cin >> e[a][b];
e[b][a] = e[a][b];
cin >> cost[a][b];
cost[b][a] = cost[a][b];
}
pre[s].push_back(s);
dis[s] = 0;
//设置dis[s]=0,从出发点开始算
for(int i=0; i<n; i++){
//u代表当前最近的点
int u=-1, mmin=inf;
for(int j=0; j<n; j++){
//从所有没被访问过的点中找出距离最近的点加入集合中
if(visit[j] == false && dis[j] < mmin){
u=j;
mmin = dis[j];
}
}
//没有多余连通的点
if(u==-1)
break;
visit[u] = true;
//以u为起始,更新整个未访问过的集合,pre存放与该节点最近相连的1个或者n个节点
for(int v=0; v<n; v++){
if(visit[v]==false && e[u][v]!=inf){
if(dis[v] > dis[u]+e[u][v]){
dis[v] = dis[u]+e[u][v];
pre[v].clear();
pre[v].push_back(u);
}
else if(dis[v] == dis[u]+e[u][v]){
pre[v].push_back(u);
}
}
}
}
dfs(d);
for(int i=path.size()-1; i>=0; i--)
cout << path[i] << " ";
cout << dis[d] << " " << mincost << endl;
}
将数组置为inf
int e[510][510];
fill(e[0], e[0]+510*510, inf);
int dis[510];
fill(e, e+510, inf);
将string全部映射为int
PAT 1034 Head of a Gang
dfs求连通域个数,图的节点是string类型,需要转化为int类型
map<int , string> intTostring;
map<string, int> stringToint;
//name1已有对应的string
if(stringToint.find(name1) != stringToint.end())
id1 = stringToint[name1];
//第一次出现的string,找一个新的数字跟他对应
else{
stringToint[name1] = now_person;
intTostring[now_person] = name1;
id1 = now_person;
now_person++;
}
二分查找(nlogn)
PAT 1044 Shopping in Mars
递增数组,从p开始查找不小于k且最接近k的数字区间段存入res
总长度为n
数组sum
void search(int p){
int start = p, end = n;
while(start < end){
int mid = (start+end)/2;
if(sum[mid] - sum[p-1] >= k)
end = mid;
else
start = mid+1;
}
int temp = sum[start] - sum[p-1];
if(temp >= k && temp <= mmax){
if(temp < mmax){
mmax = temp;
res.clear();
}
res.push_back(p);
res.push_back(start);
}
}
有回溯的dfs
寻找符合条件的多条路径,此时需要回溯
PAT1053 Path of Equal Weight (30 分)
自己一次过做出来的30分题,还有点小开心的!
//p为路径,ss为路径上的权重值,visit为是否访问
void dfs(int t){
visit[t] = 1;
ss += weight[t];
p.push_back(t);
if(node[t].size() == 0 && ss == s){
vector<int> temp;
for(int k=0; k<p.size(); k++){
temp.push_back(weight[p[k]]);
}
final.push_back(temp);
//找到一条路径,以下条件需要恢复环境
p.pop_back();
ss -= weight[t];
visit[t] = 0;
return;
}
for(int i=0; i<node[t].size(); i++){
if(visit[node[t][i]] == 0)
dfs(node[t][i]);
}
//这些条件后面的路径还会用到,所有需要恢复环境
visit[t] = 0;
ss -= weight[t];
p.pop_back();
}
set和multiset
set的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。 set和multiset的区别是:set插入的元素不能相同,但是multiset可以相同。
set中find和count的时间复杂度都为logn
求两个数组的交集数字个数
set 迭代器输出用 *iter
默认按照递增的顺序排列
int nc = 0;
for(auto it = st[id1].begin();it != st[id1].end();it++){
if(st[id2].find(*it) != st[id2].end())
nc++;//使用find,去第二个数列中查找该数出现过没有
}
求某数化为二进制后有多少位为1
int FF(int i){
int x=0;
while(i){
if(i & 1){
x++;
}
i >>= 1;
}
return x;
}
中序序列==>顺序输出
将有序序列变为完全二叉树的中序序列存储
int num[2001], res[2001];
int n, k=0;
//num存储现有的有序序列,res存储中序序列,n=size
void inorder(int t){
if(t >= n)
return;
inorder(t*2+1);
res[t] = num[k++];
inorder(t*2+2);
}
//调用
inorder(0);
使序列有序的最少交换次数
n = 数列元素个数 - 循环节个数
https://blog.csdn.net/lfb637/article/details/86653121
仅允许在相邻两元素间交换,求最少交换次数
即求逆序对==>归并排序算法可求
https://blog.csdn.net/lfb637/article/details/78309507
atoi stoi
将数字字符串转为int
atoi不会做范围检查,stoi会做范围检查,超出int范围报错runtime error
stoi(s1);
atoi(s1.c_str());
往字符串中插入0
s.insert(0, 4-s.length(), ‘0’);
边界条件
1、字符串比较时考虑字符串的长度
易忽略的点
1、C++中int和long long特别容易被忽略的点,在做乘法的时候即使单个变量在int范围内,如果乘积超了int,也需要将乘数定义为longlong 否则会出错
long double------>printf("%.2Lf", x)
2、vector可以直接使用=号来判断两个数组是否相同
3、数据为10^9时,inf要定义成0x3f3f3f3f,否则真的会死掉!
4、实在过不去,把数组开大点
5、int 10位,2^32次-1;long long 19位,2的63次-1
6、NULL——int类型默认为0,bool类型默认为false
7、int visit[205] = {0};
像这样的定义必须在循环里重新赋值为0,用fill或者for循环,否则编译器不同可能会报错
8、二维数组开到10的5次乘10的5次会内存超限,用vector,map等巧妙化解
9、有关树的题目,左右子树要乘2,所以数组大小要开到2倍以上
dfs、bfs基础模板
void dfs(int u, int l){
visit[u] = 1;
for(int i=0; i<v[u].size(); i++){
if(visit[v[u][i]] == 0)
dfs(v[u][i], l+1);
}
}
三维数组求连通域——bfs
https://blog.csdn.net/qq_34649947/article/details/81213275
//表示三个方向
int xx[6] = {0,0,0,0,-1,1};
int yy[6] = {0,0,-1,1,0,0};
int zz[6] = {-1,1,0,0,0,0};
struct node{
int x, y, z;
};
queue<node> q;
排序
当题目中涉及到插入排序,归并排序等,不需要真的去实现,用sort就可以
并查集!!
https://blog.csdn.net/zjy_code/article/details/80634149
例题:PAT 1107 Social Clusters
记录每个节点的父亲节点是哪个节点,是否属于一个门派
//初始化:初始的时候每个结点各自为一个集合,father[i]表示结点 i 的父亲结点,如果 father[i]=i,我们认为这个结点是当前集合根结点。
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
}
}
//查找:查找结点所在集合的根结点,结点 x 的根结点必然也是其父亲结点的根结点。
int get(int x) {
if (father[x] == x) { // x 结点就是根结点
return x;
}
return father[x] = get(father[x]); // 返回父结点的根结点,并另当前结点父结点直接为根结点
}
//合并:将两个元素所在的集合合并在一起,通常来说,合并之前先判断两个元素是否属于同一集合。
void merge(int x, int y) {
x = get(x);
y = get(y);
if (x != y) { // 不在同一个集合
father[y] = x;
}
}
调整大顶堆
把数组中最大的数字调整到最上面,并且把下面的恢复成大顶堆的样子
因为本来就是大顶堆,只是第一个数字变了,所以影响的只是单路上的数字
//法一
void downAdjust(vector<int> &b, int low, int high) {
int i = 1, j = i * 2;
while(j <= high) {
if(j + 1 <= high && b[j] < b[j + 1]) j = j + 1;
if (b[i] >= b[j]) break;
swap(b[i], b[j]);
i = j; j = i * 2;
}
}
//法2
//将元素k为根的子树进行调整
void HeadAdjust(int a[], int k, int len){
a[0] = a[k];
for(int i=2*k; i<=len; i*=2){
if(i<len && a[i]<a[i+1])
i++;
if(a[0] >= a[i])
break;
else{
a[k] = a[i];
k=i;
}
}
a[k] = a[0];
}
//构建大顶堆
void BUildmaxHeap(int a[], int len){
for(int i=len/2; i>0; i--)
HeadAdjust(a, i, len);
}
//堆排序
void HeapSort(int a[], int len){
BUildmaxHeap(a, len);
for(int i=len; i>1; i--){
swap(a[i], a[1]); //输出堆顶元素(和堆底元素交换)
HeadAdjust(a, 1, i-1); //调整,把剩余的i-1个元素整理成堆
}
}
判断输入是否符合规定小数要求
PAT 1108 Finding Average
//要求判断输入是否为保留两位的小数
double temp;
char a[50], b[50];
scanf("%s", a);
sscanf(a,"%lf",&temp); //把a的值以double格式赋给temp。如果输入的不是数字,temp会继续保留之前的数字
sprintf(b,"%.2f",temp);//temp按%.2f写到b
//比较a和b是否一致即可
平衡二叉树
总结模版如下
https://blog.csdn.net/PickCake/article/details/116133191
The lowest common ancestor (LCA)最近公共祖先
1151 LCA in a Binary Tree (30 分)
不需要建树,一般性建树都会超时,或者段错误
一般会给定先序遍历和中序遍历序列
已知某个树的根结点,若a和b在根结点的左边,则a和b的最近公共祖先在当前子树根结点的左子树寻找,如果a和b在当前子树根结点的两边,在当前子树的根结点就是a和b的最近公共祖先,如果a和b在当前子树根结点的右边,则a和b的最近公共祖先就在当前子树的右子树寻找。中序加先序可以唯一确定一棵树,在不构建树的情况下,在每一层的递归中,可以得到树的根结点,在此时并入lca算法可以确定两个结点的公共祖先~
void lca(int inl, int inr, int prel, int a, int b){
if(inl > inr)
return;
int inRoot = pos[pre[prel]], ain = pos[a], bin = pos[b];
if(ain < inRoot && bin < inRoot)
lca(inl, inRoot-1, prel+1, a, b);
else if((ain < inRoot && bin > inRoot) || (ain > inRoot && bin < inRoot))
printf("LCA of %d and %d is %d.\n", a, b, in[inRoot]);
else if(ain > inRoot && bin > inRoot)
lca(inRoot+1, inr, prel+1+(inRoot-inl), a, b);
else if(ain == inRoot)
printf("%d is an ancestor of %d.\n", a, b);
else if (bin == inRoot)
printf("%d is an ancestor of %d.\n", b, a);
}