赞
踩
v1.6
最近更新:2021/12/9
#include<cstdio> #include<cstring> #include<stdlib.h> #include<algorithm> #include<iostream> #include<vector> #include<set> #include<string> #include<map> #include<queue> #include<stack> #include<math.h> using namespace std; const int INF = 0x3f3f3f3f; 注:math.h中,abs(x)求整数x绝对值, fabs(x)求double型x绝对值, ceil(x)取上整,floor(x)取下整, pow(x,y) x的y次方,sqrt(x)开根 fabs、ceil、floor、pow、sqrt均返回double型
void quickSort(int a[],int l,int r){ //快速排序 if(l < r){ int pos = partition(a, l, r); quickSort(a, l, pos-1); quickSort(a, pos+1, r); } } int partition(int a[],int l,int r){ //快排的一趟 int temp = a[l]; while(l < r){ while (l < r && temp <= a[r]) r--; a[l] = a[r]; while(l < r && temp >= a[l]) l++; a[r] = a[l]; } a[l] = temp; return l; }
//非递归实现 void merge(int A[], int L1, int R1, int L2, int R2){ //将数组A的有序的[L1,R1]与[L2,R2]区间合并为一个有序区间。 int i = L1, j = L2; int temp[maxn], index = 0; while(i <= R1 && j <= R2){ if(A[i] <= A[j]){ temp[index++] = A[i++]; } else{ temp[index++] = A[j++]; } } while(i <= R1) temp[index++] = A[i++]; while(j <= R2) temp[index++] = A[j++]; for(i = 0; i < index; i++){ A[L1 + i] = temp[i]; } } void mergeSort(int A[]){ //主。额外注明:元素个数为n,数组下标从1开始。 for(int step = 2; step / 2 <= n; step *= 2){ //step表示当前要合并的一对长度和的最大值 for(int i = 1; i <= n; i += step){ int mid = i + step / 2 - 1; if(mid + 1 <= n){ merge(A, i, mid, mid+1, min(i + step - 1, n)); } } } }
//假设不含重复元素,在递增序列A中进行二分查找,目标为x,找不到返回-1 int binarySearch(int A[], int left, int right, int x){ int mid; while(left <= right){ mid = (left + right) / 2; if(A[mid] == x) return mid; else if(A[mid] > x) right = mid - 1; else left = mid + 1; } return -1; } //假设可能含重复元素,lower_bound返回第一个值 >= x 的元素的位置 L, //upper_bound返回第一个值 > x 的元素的位置 R //如果A的最后一个元素是x,则x的个数是R-L+1。其余任何情况x的个数都是R-L。 int lower_bound(int A[], int left, int right, int x){ int mid; while(left < right){ mid = (left + right) / 2; if(A[mid] >= x) right = mid; else left = mid + 1; } return left; } int upper_bound(int A[], int left, int right, int x){ int mid; while(left < right){ mid = (left + right) / 2; if(A[mid] > x) right = mid; else left = mid + 1; } return left; }
typedef long long LL; //快速幂的递归写法,计算a^b % m LL binaryPow(LL a, LL b, LL m){ if(b == 0) return 1; if(b & 1) return a * binaryPow(a, b - 1, m) % m; else{ LL mul = binaryPow(a, b / 2, m); return mul * mul % m; } } //快速幂的迭代写法,计算a^b % m LL binaryPow(LL a, LL b, LL m){ LL ans = 1; while(b > 0){ if(b & 1) ans = ans * a % m; a = a * a % m; b >>= 1; //注:等价于b = b >> 1; 相当于b除以2 } return ans; }
4、二分查找(二分使用该板子)
二分的本质不是单调性,能二分不一定要有单调性。二分的本质如下:假设有一整数序列,有一性质,使得某一个数右边(含该数)满足该性质,而该数左边(不含该数)不满足该性质。形象地表示,假设A是不满足该性质的数,B是满足该性质的数
AAAAAABBBBBBB
则二分可以寻找该性质的分界点,即既能寻找最右边的A,又能寻找最左边的B。
情况1:找最右边的A
mid = (l + r + 1) / 2; //注意加一!
if(检查mid是否满足A的性质)
如果true,答案在[mid, r],令l=mid即可。
如果false,答案在[l, mid-1],令r=mid - 1即可。
情况2:找最左边的B
mid = (l + r) / 2;
if(检查mid是否满足B的性质)
如果true,答案[l,mid],令r = mid即可。
如果false,答案[mid+1,r],令l=mid+1即可。
(以上循环条件l < r,注:循环结束时一定有 l = r)
现在解释为什么情况1里mid那要+1:当l=r-1时,mid = (l+r)/2 是等于l的,如果检查条件返回true,则死循环。因此补上+1。二分时,mid左右偏移一点完全不影响效率和正确性(只要不死循环)。
二分一定会停止,之后判断是否为题目中的无解情况,即判断序列[l]号元素是否满足要求。
以上情况对应的二分板子如下(推荐该板子,情况完备且有定式):
// 情况1:找最右边的A时使用: int bsearch_1(int l, int r) { while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; // check()判断mid是否满足A的性质 else r = mid - 1; } return l; } // 情况2:找最左边的B时使用: int bsearch_2(int l, int r) { while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; // check()判断mid是否满足B的性质 else l = mid + 1; } return l; } //注:check()判断 是否满足需要找的那个点所在分类的条件,满足为True
记忆:
找最右 ~ mid多加1 ~ 左为mid(check条件后跟的是l=mid)
找最左 ~ mid不加1 ~ 右为mid
struct node{ int data; node* lchild; node* rchild; }; node* newNode(int v){ //生成新结点,v为结点权值 node* Node = new node; Node->data = v; Node-> lchild = Node->rchild = NULL; return Node; } void search(node* root,int x,int newdata){ //查找值为x的所有结点,并把它们的值修改为newdata if(root == NULL){ return; } if(root->data == x){ root->data = newdata; } search(root->lchild,x,newdata); search(root->rchild,x,newdata); } void insert(node* &root,int x){ //插入值为x的新结点,查找失败的位置即插入位置,注意引用 if(root == NULL){ root = newNode(x); return; } if(x应该插在左子树){ insert(root->lchild,x); } else{ insert(root->rchild,x); } } node* Create(int data[],int n){ //创建二叉树,插入的数据在数组中 node* root = NULL; for(int i = 0; i < n; i++){ insert(root,data[i]); } return root; }
void preorder(node* root){ //先序遍历 根左右 if(root == NULL){ return; } visit(root); preorder(root->lchild); preorder(root->rchild); } void inorder(node* root){ //中序遍历 左根右 if(root == NULL){ return; } inorder(root->lchild); visit(root); inorder(root->rchild); } void postorder(node* root){ //后序遍历 左右根 if(root == NULL){ return; } postorder(root->lchild); postorder(root->rchild); visit(root); } void LayerOrder(node* root){ //层序遍历,为记录结点所在层数,在二叉树结构体中添加 int layer; queue<node*> q; root->layer = 1; q.push(root); while(!q.empty()){ node* now = q.front(); q.pop(); visit(now); if(now->lchild != NULL){ now->lchild->layer = now->layer + 1; q.push(now->lchild); } if(now->rchild != NULL){ now->rchild->layer = now->layer + 1; q.push(now->rchild); } } }
node* create(int preL,int preR,int inL,int inR){ //给定先序遍历序列pre[]、中序遍历序列in[],重建二叉树。[preL,preR]为先序序列区间,[inL,inR]为中序序列区间。 if(preL > preR){ return NULL; } node* root = new node; root->data = pre[preL]; int k; for(k = inL; k <= inR; k++){ if(in[k] == pre[preL]){ break; } } int numLeft = k - inL; root->lchild = create(preL + 1,preL + numLeft,inL,k-1); root->rchild = create(preL + numLeft + 1,preR,k + 1,inR); return root; }
//(1)二叉查找树的插入与建立 void insert(node* &root, int x){ //将x插入以root为根的二叉查找树中,递归地 if(root == NULL){ root = newNode(x); return; } if(x == root->data){ return; } else if(x < root->data){ insert(root->lchild, x); } else{ insert(root->rchild, x); } } node* Create(int data[], int n){ //将data[]构造成一棵二叉查找树,返回根结点指针 node* root = NULL; for(int i = 0; i < n; i++){ insert(root, data[i]); } return root; } //(2)二叉查找树的查找 void search(node* root, int x){ //查找数据为x的结点 if(root == NULL){ printf("search failed\n"); //查找失败 return; } if(x == root->data){ //查找成功,在此处进行访问 } else if(x < root->data){ search(root->lchild, x); } else{ search(root->rchild, x); } } //(3)二叉查找树的删除,复杂一些。思路:删结点a -> 用a的前驱(或后继)替代a,删除a的前驱(或后继)。边界:删除叶子。递归进行 node* findMax(node* root){ //辅助函数:寻找以root为根的二叉查找树中最大结点 while(root->rchild != NULL){ root = root->rchild; } return root; } node* findMin(node* root){ //辅助函数:寻找以root为根的二叉查找树中最小结点 while(root->lchild != NULL){ root = root->lchild; } return root; } void deleteNode(node* &root, int x){ //在以root为根的二叉查找树中删除值为x的结点。 if(root == NULL){ //边界:不存在值为x的结点 return; } if(root->data == x){ //找到了x,删除它 if(root->lchild == NULL && root->rchild == NULL){ //边界:叶子结点,直接删除 root = NULL; } else if(root->lchild != NULL){ //左结点不空,就找前驱递归 node* pre = findMax(root->lchild); root->data = pre->data; deleteNode(root->lchild, pre->data); } else if(root->rchild != NULL){ //右结点不空,就找后继递归 node* next = findMin(root->rchild); root->data = next->data; deleteNode(root->rchild, next->data); } } else if(x < root->data){ //往左查找 deleteNode(root->lchild, x); } else if(x > root->data){ //往右查找 deleteNode(root->rchild, x); } }
struct node{ int v, height; node *lchild, *rchild; }; node* newNode(int v){ //用于生成新结点 node* Node = new node; Node->v = v; Node->height = 1; Node->lchild = Node->rchild = NULL; return Node; } int getHeight(node* root){ //获得以root结点为根的子树的高度 if(!root) return 0; return root->height; } int getBalanceFactor(node* root){ //获得root结点的平衡因子 return getHeight(root->lchild) - getHeight(root->rchild); } void updateHeight(node* root){ //更新root的height值 root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1; } void L(node* &root){ //对root进行左旋操作。即root的右结点作新的根,root作为其左结点,其原来的左结点作为root的右结点 node* temp = root->rchild; root->rchild = temp->lchild; temp->lchild = root; updateHeight(root); updateHeight(temp); root = temp; } void R(node* &root){//对root进行右旋操作。即root的左结点作新的根,root作为其右结点,其原来的右结点作为root的左结点 node* temp = root->lchild; root->lchild = temp->rchild; temp->rchild = root; updateHeight(root); updateHeight(temp); root = temp; } void insert(node* &root, int v){ //插入权值为v的结点 if(!root){ root = newNode(v); return; } if(v < root->v){ //v比root小,则往左插 insert(root->lchild, v); //往左递归插入 updateHeight(root); //更新树高 if(getBalanceFactor(root) == 2){ //如果失衡则进行相应调整 if(getBalanceFactor(root->lchild) == 1){ //LL型 R(root); } else if(getBalanceFactor(root->lchild) == -1){ //LR型 L(root->lchild); R(root); } } } else{ //v比root大,以下同理。 insert(root->rchild, v); updateHeight(root); if(getBalanceFactor(root) == -2){ if(getBalanceFactor(root->rchild) == -1){ //RR型 L(root); } else if(getBalanceFactor(root->rchild) == 1){ //RL型 R(root->rchild); L(root); } } } } node* create(int data[], int n){ //建立AVL树 node* root = NULL; for(int i = 0; i < n; i++){ insert(root, data[i]); } return root; }
struct node{
int data;
vector<int> child;
} Node[maxn];
//新建一个结点
int index = 0;
int newNode(int v){
Node[index].data = v;
Node[index].child.clear();
return index++;
}
void PreOrder(int root){ //树的先根遍历 根 子树,注:递归边界隐含在for循环的结束条件中,但是调用传入的root不能是空结点。 visit(Node[root]); for(int i = 0; i < Node[root].child.size(); i++){ PreOrder(Node[root].child[i]); } } void LayerOrder(int root){ //树的层序遍历,为记录结点所在层数,在树的结构体中添加 int layer; queue<int> Q; Q.push(root); Node[root].layer = 0; while(!Q.empty()){ int front = Q.front; Q.pop(); visit(Node[front]); for(int i = 0; i < Node[front].child.size(); i++){ int child = Node[front].child[i]; Node[child].layer = Node[front].layer + 1; Q.push(child); } } }
//以大顶堆为例,堆是一棵完全二叉树,用数组存储,根节点在数组中下标为1,结点i的左子树下标为 i * 2,右子树下标为i * 2 + 1(如果存在) //建堆:对于一棵乱序的完全二叉树,通过调整使其成为堆。由于用的是数组存储,是在一个乱序序列上建堆。 //设n为元素个数,heap[]是表示堆的数组 void createHeap(){ //时间复杂度:O(n) for(int i = n/2; i>= 1; i--){ downAdjust(i,n); } } void downAdjust(int low, int high){ //向下调整,对heap在[low,high]范围内进行向下调整(high一般设置为n)。这一趟把low往下调整到正确位置。 int i = low, j = i * 2; while(j <= high){ if(j+1 <= high && heap[j+1] > heap[j]){ j = j + 1; } if(heap[j] > heap[i]){ swap(heap[j], heap[i]); i = j; j = i * 2; } else{ break; } } } //删除堆顶元素:用最后一个元素覆盖堆顶元素,再对根节点向下调整。时间复杂度O(logn) void deleteTop(){ heap[1] = heap[n]; n--; downAdjust(1, n); } //往堆中添加一个元素:把添加的元素放在最后,进行向上调整。 void insert(int x){ n++; heap[n] = x; upAdjust(1, n); } void upAdjust(int low, int high){ //向上调整,对heap在[low,high]范围内进行向上调整(low一般设置为1) int i = high, j = i / 2; while(j >= low){ if(heap[j] < heap[i]){ swap(heap[j], heap[i]); i = j; j = i / 2; } else{ break; } } } /*堆排序*/ //思路:(以大顶堆为例)建堆后,取出堆顶元素,将堆的最后一个元素移至堆顶,对堆顶向下调整,直到堆只有一个元素。 //给定一个初始乱序数组序列heap,调整后使其有序 void heapSort(){ createHeap(); for(int i = n; i > 1; i--){ swap(heap[i], heap[1]); downAdjust(1, i - 1); } }
int father[maxn]; //father[i]表示i的父亲结点,若father[i] == i,则i是该集合的根结点。一个集合是一棵树(根有自环) //初始化:father[i] = i; int findfather(int x){ //(简单版)查找。返回元素x所在集合的根结点 while(x != father[x]){ x = father[x]; } return x; } int findfather(int x){ //(路径压缩版)查找。均摊后时间复杂度降低为近似O(1)。 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 Union(int a, int b){ //合并。合并集合a、集合b。 注:Union中U必须大写,否则是关键字。 int faA = findfather(a); int faB = findfather(b); if(faA != faB){ father[faA] = faB; } }
//1邻接矩阵 int G[maxn][maxn]; //2邻接表,一个表存放一个顶点的所有出边 //第一种:邻接表只存边的终点编号,不存边权 vector<int> Adj[maxn]; //Adj有maxn个vector,vector元素为int,存放结点的出边终点。 //第二种:邻接表存边的终点编号、边权 struct Node{ int v; //出边的终点的编号 int w; //边权 Node(int _v, int _w){ //构造函数 v = _v; w = _w; } }; vector<Node> Adj[maxn]; //Adj有maxn个vector,vector元素为Node,存放结点的出边终点以及对应边权。
const int maxn = 1000; const int INF = 0x3f3f3f3f; //2.1邻接矩阵版 int n,G[maxn][maxn]; //n为顶点数 bool vis[maxn] = {false}; void DFS(int u, int depth){ //u为顶点编号,depth为深度,从1开始 vis[u] = true; //在此进行对u顶点的操作 for(int v = 0; v < n; v++){ if(vis[v] == false && G[u][v] != INF){ DFS(v, depth + 1); } } } void DFSTrave(){ for(int u = 0; u < n; u++){ if(vis[u] == false){ DFS(u, 1); } } } //2.2邻接表版(使用第二种邻接表,即邻接表存边的终点编号、边权) vector<Node> Adj[maxn]; int n; //n为顶点数 bool vis[maxn] = {false}; void DFS(int u, int depth){ vis[u] = true; //在此进行对u顶点的操作 for(int i = 0; i < Adj[u].size(); i++){ int v = Adj[u][i].v; if(vis[v] == false){ DFS(v, depth + 1); } } } void DFSTrave(){ for(int u = 0; u < n; u++){ if(vis[u] == false){ DFS(u, 1); } } }
//3.1邻接矩阵版 int n,G[maxn][maxn]; bool vis[maxn] = {false}; void BFS(int u){ queue<int> q; q.push(u); vis[u] = true; while(!q.empty()){ int now = q.front(); q.pop(); //在此对now顶点进行操作 for(int v = 0; v < n; v++){ if(vis[v] == false && G[now][v] != INF){ q.push(v); vis[v] = true; } } } } void BFSTrave(){ for(int u = 0; u < n; u++){ if(vis[u] == false){ BFS(u); } } } //3.2邻接表版 vector<Node> Adj[maxn]; int n; bool vis[maxn] = {false}; void BFS(int u){ queue<int> q; q.push(u); vis[u] = true; while(!q.empty()){ int now = q.front(); q.pop(); //在此对now顶点进行操作 for(int i = 0; i < Adj[now].size(); i++){ int v = Adj[now][i].v; if(vis[v] == false){ q.push(v); vis[v] = true; } } } } void BFSTrave(){ for(int u = 0; u < n; u++){ if(vis[u] == false){ BFS(u); } } }
//4.1 输入图G后,使用Dijkstra 获得起点s到其他节点所有最短路长 + 前驱 //(邻接表版Dij) vector<Node> Adj[maxn]; int d[maxn]; //记录起点到结点i的最短距离 vector<int> pre[maxn]; //pre[i]表示起点到结点i的最短路径里,结点i的前驱结点 bool vis[maxn] = {false}; void Dij(int s){ //s为起点 fill(d, d + maxn, INF); //memset(d, INF, sizeof(d)); 头文件cstring d[s] = 0; for(int i = 0; i < n; i++){ int u = -1, MIN = INF; //找出未访问的点里最小的 for(int j = 0; j < n; j++){ if(vis[j] == false && d[j] < MIN){ u = j; MIN = d[j]; } } if(u == -1) return; //剩下的结点不连通,结束 vis[u] = true; for(int j = 0; j < Adj[u].size(); j++){ int v = Adj[u][j].v; if(vis[v] == false){ if(d[u] + Adj[u][j].w < d[v]){ d[v] = d[u] + Adj[u][j].w; pre[v].clear(); pre[v].push_back(u); } else if(d[u] + Adj[u][j].w == d[v]){ pre[v].push_back(u); } } } } } //4.2 起点->终点有多条最短路,在其中找到使第二标尺最优的路径,使用DFS //前提:在4.1的基础上,设st为路径起点。 int optvalue = 初值0或INF; //第二标尺最优值 vector<int> path, temppath; //最优路径、临时路径,倒着存的 void DFS(int v){ //初始调用传入终点编号 if(v == st){ //边界:到达起点 temppath.push_back(v); int value; //此处计算temppath上的value值,涉及边权或者点权 if(value优于optvalue){ optvalue = value; path = temppath; } temppath.pop_back(); return; } temppath.push_back(v); for(int i = 0; i < pre[v].size(); i++){ DFS(pre[v][i]); } temppath.pop_back(); }
//思想:若存在点k,使得k作中介能使i->j的最短路径缩短,则用k作中介。可以有负边权,但不能有负权回路。 //设n为结点数。时间复杂度O(n^3),限制了n<=200 int dis[maxn][maxn];//dis[i][j]初始存i->j的边权,Floyd运算后dis[i][j]表示i->j的最短距离 void Floyd(){ for(int k = 0; k < n; k++){ for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ if(dis[i][k] + dis[k][j] < dis[i][j] && dis[i][k] != INF && dis[k][j] != INF){ dis[i][j] = dis[i][k] + dis[k][j]; } } } } } /* 附:dis[][]的初始化过程:1、全部赋INF 2、dis[i][i] = 0 此外,输入dis[i][j]的值为 w 时,若为无向图,记得dis[j][i] 也为 w */
//邻接表实现。(1)拓扑序列:若图中存在边u->v,则序列中u在v前面。(2)拓扑排序可判断图是否为有向无环图。 vector<int> Adj[maxn]; //拓扑排序和权值没关系,此处以第一种邻接表为例 int n, m, inDegree[maxn]; //顶点数,边数,每个结点的入度 bool tppx(){ //返回true说明拓扑排序成功,图是有向无环图。否则图中有环。 int num = 0; queue<int> q; for(int i = 0; i < n; i++){ //入度为0入队 if(inDegree[i] == 0){ q.push(i); } } while(!q.empty()){ int u = q.front(); q.pop(); //访问该结点,如printf("%d",u); for(int i = 0; i < Adj[u].size(); i++){ int v = Adj[u][i]; inDegree[v]--; if(inDegree[v] == 0){ q.push(v); } } //可清空u的所有出边,也可不写。Adj[u].clear(); num++; } if(num == n) return true; else return false; } //注:若题目要求有多个入度为0的点选择编号最小的,则把queue改成priority_queue,保证队首元素是最小的。
struct bign{ int d[1000]; //数的低位存在低下标,如235813存储为 d[0]~d[5] 分别为 318532 int len; //记录长度 bign(){ //构造函数,方便初始化 memset(d,0,sizeof(d)); len = 0; } }; bign change(char str[]){ //读入字符数组,转化为大整数bign bign a; a.len = strlen(str); for(int i = 0; i < a.len; i++){ a.d[i] = str[a.len - 1 - i] - '0'; } return a; } int compare(bign a, bign b){ //比较大整数a与b的大小。 a>b,a=b,a<b 分别返回 1,0,-1 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.d[i] > b.d[i]) return 1; else if(a.d[i] < b.d[i]) return -1; } return 0; } } void print(bign a){ //输出大整数 for(int i = a.len - 1; i >= 0; i--){ printf("%d", a.d[i]); } } bign add(bign a, bign b){ //大整数加法 bign c; int carry = 0; for(int i = 0; i < a.len || i < b.len; i++){ int temp = a.d[i] + b.d[i] + carry; c.d[c.len++] = temp % 10; carry = temp / 10; } if(carry != 0){ c.d[c.len++] = carry; } return c; } bign sub(bign a, bign b){ //大整数减法a-b bign c; for(int i = 0; i < a.len || i < b.len; i++){ if(a.d[i] < b.d[i]){ a.d[i + 1]--; a.d[i] += 10; } c.d[c.len ++] = a.d[i] - b.d[i]; } while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){ c.len--; } return c; } bign multi(bign a, int b){ //大整数 乘以 普通整数 bign c; int carry = 0; for(int i = 0; i < a.len; i++){ int temp = a.d[i] * b + carry; c.d[c.len++] = temp % 10; carry = temp / 10; } while(carry != 0){ c.d[c.len++] = carry % 10; carry /= 10; } return c; }
int gcd(int a, int b){ //求a与b的最大公约数,a无须大于b
if(b == 0) return a;
else return gcd(b, a % b);
}
//a、b的最小公倍数 = a / 最大公约数 * b
struct Fraction{ //用long long更好 int up, down; //规定1.分子up可以是负数,但分母down是非负数。2.若分数为0,规定up=0,down=1。3、分子分母互素(公约数只有1) } Fraction reduction(Fraction result){ //化简与约分,使一个分数满足上述三条规定 if(result.down < 0){ //若分母是负数,则分母变成非负数 result.up = -result.up; result.down = -result.down; } if(result.up == 0){ //若分数为0,则分母置1 result.down = 1; }else{ //约分,分子分母除以它们的最大公约数即可 int d = gcd(abs(result.up), result.down); //d为分子分母的最大公约数 result.up /= d; result.down /= d; } return result; } void print(Fraction r){ //打印分数r r = reduction(r); if(r.down == 1){ //是整数(含0) printf("%lld", r.up); } else if(abs(r.up) > r.down){ //是假分数 printf("%d %d/%d",r.up / r.down, abs(r.up) % r.down, r.down); } else{ //是真分数 printf("%d/%d", r.up, r.down); } } 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); }
//题意:给定中缀表达式,计算值。 /*思路:1中缀转后缀:队列存后缀。顺序扫描中缀,若是操作数直接入队; 若是操作符op若优先级高则入栈,若优先级低或等于则弹栈至队列直到op优先级高于栈顶或栈空。扫描完后栈中剩余元素入队。 2计算后缀:队列顺序出队,若是操作数入栈,若是操作符弹栈两个数(后弹的是第一操作数),运算结果入栈。扫描完栈中只有一个数即是答案*/ struct node{ double num; char op; bool flag; //1表示是操作数,0表示是操作符 }; string str; //输入的表达式 stack<node> s; //操作符栈 queue<node> q; //后缀表达式 map<char,int> op; //0、初始化(使用时请放main函数里) op['+'] = op['-'] = 1; op['*'] = op['/'] = 2; for(string::iterator it = str.end(); it != str.begin(); it--){ //去掉str中所有空格 if(*it == ' ') str.erase(it); } void Change(){ //1、中缀转后缀 double num; node temp; for(int i = 0; i < str.size(); ){ if(str[i] >= '0' && str[i] <= '9'){ //若是操作数 temp.flag = true; temp.num = str[i] - '0'; i++; while(i < str.size() && str[i] >= '0' && str[i] <= '9'){ temp.num = temp.num * 10 + str[i] - '0'; i++; } q.push(temp); } else{ //若是操作符 temp.flag = false; while(!s.empty() && op[str[i]] <= sp[s.top().op]){ q.push(s.top()); s.pop(); } temp.op = str[i]; s.push(temp); i++; } } while(!s.empty()){ //剩余操作符入栈 q.push(s.top()); s.pop(); } } double Cal(){ //2、计算后缀,返回答案 double temp1, temp2; node cur, temp; while(!q.empty()){ cur = q.front(); q.pop(); if(cur.flag == true){ //若是操作数,直接入栈 s.push(cur); } else{ //若是操作符 temp2 = s.top().num; //第二操作数 s.pop(); temp1 = s.top().num; //第一操作数 s.pop(); temp.flag = true; if(cur.op == '+') temp.num = temp1 + temp2; else if(cur.op == '-') temp.num = temp1 - temp2; else if(cur.op == '*') temp.num = temp1 * temp2; else if(cur.op == '/') temp.num = temp1 / temp2; s.push(temp); } } return s.top().num; }
文本串中匹配模式串 O(n+m)
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度,下标从1开始 //求模式串的Next数组: for (int i = 2, j = 0; i <= m; i ++ ) { while (j && p[i] != p[j + 1]) j = ne[j]; if (p[i] == p[j + 1]) j ++ ; ne[i] = j; } // 匹配 for (int i = 1, j = 0; i <= n; i ++ ) { while (j && s[i] != p[j + 1]) j = ne[j]; if (s[i] == p[j + 1]) j ++ ; if (j == m) { j = ne[j]; // 匹配成功后的逻辑 } }
//求 m^k mod p,时间复杂度 O(logk)。
int qmi(int m, int k, int p)
{
int res = 1, t = m;
while (k)
{
if (k&1) res = res * t % p;
t = t * t % p;
k >>= 1;
}
return res;
}
//判断x是不是质数,时间复杂度O(根号n)
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
//形式:24 = 2 ^ 3 * 3 ^ 1,时间复杂度O(根号n)
void divide(int x)
{
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
int s = 0;
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl;
}
if (x > 1) cout << x << ' ' << 1 << endl;
cout << endl;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。