说在前面
算法应该是大二上我学的最轻松的一门课了,我有一个完美的开始和一个完美的结束——尽管中间一度每周打算签完到就不做了,非常感谢狗头学长的板子(C++算法板子积累 - Only(AR) 的编程日记 ),也特别感谢各位助教对我的帮助。下面的板子转pdf打印出来大概有90多页,虽然大部分都用不上,但是!有总比没有好嘛——
Tips
图论
重边:最短路记得判断,只存最短的边
负环?负边权?
有重边的情况下判断负环:有负数就存负数,负数越小越好;否则存正数,正数越大越好
最短路径
单源最短路径:一个点到其他任意点的最短路径
Bellman-Ford算法:时间$O(VE)$;可负权重 ;可回路 ;可以检测负环 ,但不能在存在负环的图中计算单源最短路径。
有向无环图中的单源最短路径问题:时间$O(V+E)$;可负权重 ;不可回路
Dijkstra算法:时间$O((V+E)*logV)$;不可负权重;可回路
所有结点对的最短路径问题:所有点到所有点的最短路径
floyd算法:时间$O(n^3)$空间$O(n^2)$;可负权重 ;可回路 ;不能检测负环,不能有负环
特别地,可以用BFS 求无权图最短路径
最大流:在一个流网络中,找到从源点到汇点的最大流量。流网络是一个有向图,每条边有一个容量限制,表示通过该边的最大流量。
Edmonds-Karp算法:时间 $O(VE^{2})$;适合稀疏 图
Dinic算法:时间 $O(V^{2}E)$;适合稠密 图
最大二分匹配:在二分图中找到最大的匹配数。二分图是一种特殊的图,其节点可以分为两个互不相交的集合,使得每条边都连接这两个不同集合中的节点。
Dinic最小割/最大流算法:时间 $O(V^2E)$
匈牙利算法:时间 $O(VE)$
最小生成树:在连通加权图中,找到一棵包含所有节点的树,使得树中所有边的权重之和最小。目的是找到连接所有顶点的最小总权重的边集。不关心顶点之间的具体路径长度,只关心整体结构的权重最小。
Prim:时间 朴素版 $O(V^2)$,堆优化版 $O(ElogV)$
Kruskal:时间 $O(ElogE)$
有向无环图 $G(V,E)$,$G$ 是半连通的当且仅当有一条路径,这条路径上有图 $G$ 中所有点:所以判断一个图是不是半连通的只需要判断拓扑序列的相邻节点是否有边
做题思想/技巧
注意事项
在对接近 0 的负数四舍五入时应输出 0.00
而非 -0.00
:fabs(a) < 0.005
时输出 0.00
快速读写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 inline ll read () { ll s=0 ,w=1 ; char ch=getchar (); while (ch<'0' ||ch>'9' ){if (ch=='-' )w=-1 ;ch=getchar ();} while (ch>='0' &&ch<='9' ) s=s*10 +ch-'0' ,ch=getchar (); return s*w; }inline void out (ll a) { if (a>=10 )out (a/10 ); putchar (a%10 +'0' ); }int main () { ll n; n=read (); out (n); return 0 ; }
数组大小
如无向图双倍空间,FFT四倍空间
空间计算 sizeof
,如下方法计算 数组所占空间(KB): cout << sizeof a/1024
corner case
$n = 0,1$
$a_i = 0, 1e9, -1e9$
几何中斜率为0
初始化
多测清空,不要滥用 memset
memset(a, 0, sizeof(int)*(n+1));
正确
memset(a, 0, sizeof a);
超时
以及 memset
初始化最大值 应为 memset(a, 0x3f, sizeof(int) * (n+1));
long long
cin和cout关闭同步流(但关闭后不能用scanf printf等c语言的输入输出)
ios::sync_with_stdio(false)
cin, cout时用 ‘\n’ 代替 endl
数组 是否够大
浮点数误差 :如几何 求面积能否直接用整数计算
不要用gets!!!
int max: 2147483647, which is 2^31 - 1
int min: -2147483648, which is -2^31
long long max: 9223372036854775807, which is 2^63 - 1
long long min: -9223372036854775808, which is -2^63
一些最大值最小值
用 climits
头文件 INT_MAX
INT_MIN
LLONG_MAX
LLONG_MIN
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> #include <climits> int main () { std::cout << "int 的最大值是:" << INT_MAX << std::endl; std::cout << "int 的最小值是:" << INT_MIN << std::endl; std::cout << "long long 的最大值是:" << LLONG_MAX << std::endl; std::cout << "long long 的最小值是:" << LLONG_MIN << std::endl; return 0 ; }
cppStart
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> #include <algorithm> #include <cmath> #include <string> #include <vector> #include <queue> #include <stack> #include <set> #include <climits> using namespace std; int main () { return 0 ; }
标准库
能按索引访问 元素的容器:vector
能遍历 的容器:vector
, set
, multiset
vector
(向量): 动态数组;当需要随机访问 元素且频繁在末尾添加或删除元素时。
queue
(队列): FIFO的数据结构;当需要按照添加顺序处理元素时,如广度优先搜索(BFS)。
priority_queue
(优先队列): 自动排序 ;当需要处理具有优先级的任务时,如最小生成树算法(Prim’s)或处理事件驱动的系统。
stack
(栈): LIFO;当需要后进先出的处理顺序时,如深度优先搜索(DFS)、递归算法的辅助数据结构。
set
(集合): 自动排序 ,不包含重复元素 ;当需要存储唯一元素并经常进行查找操作时,如去重、集合运算。
multiset
(多重集合): 与 set
类似,但允许存储重复的元素 ;当需要存储元素并保持有序 ,但元素可以重复时。
algorithm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 __gcd(a, b) __builtin_popcount(a) is_sorted (a, a + n) is_sorted_until (a, a + n) sort (a, a + n) sort (a, a + n, greater <int >()) stable_sort (a, a + n) nth_element (a, a + k, a + n) unique (begin, end) max (a, b) min (a, b) max_element (a, a + n) min_element (a, a + n) int pos1 = lower_bound (a, a + n, key)-a; int pos2 = upper_bound (a, a + n, key)-a; binary_search (a, a + n, key) is_heap (a, a + n) is_heap_until (a, a + n) make_heap (a, a + n) push_heap (a, a + n) pop_heap (a, a + n) sort_heap (a, a + n) is_permutation () next_permutation () prev_permutation () fill (a, a + n, val) reverse (a, a + n) auto it = find (shuzu, shuzu+n, 1 )auto it = find (v1.begin (), v1.end (), 1 )
如
1 2 3 4 int a[10 ] = {5 ,2 ,3 ,4 ,5 }; int b[10 ] = {5 ,2 ,3 ,4 ,1 }; cout << is_permutation (a, &a[5 ], b);is_permutation (c1.begin ()+1 , c1.end (), c2.begin ());
vector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 vector <int > v1 vector <int > v2 (5 ,0 ); vector<int > v3 (v2.begin(), v2.end()) ; vector<int > v4 (v2) ; v.at (k) v.front () v.back () v.begin () v.end () v.empty () v.size () v.max_size () v.clear () v.insert (pos, item) v.eraze (pos) v.push_back (item) v.pop_back () v.reserve (n); vector<int > v; v.push_back (3 );for (int i=0 ;i<=v.size ()-1 ;i++) { cout<<v[i]<<" " ; }for (int i=0 ;i<=(int )v.size ()-1 ;i++) { cout<<v[i]<<" " ; }
queue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 queue<int > q; q.push (item) q.front () q.pop () q.back () q.empty () q.size () q.emplace (item) priority_queue<int , vector<int >, greater<int >> pq pq.top () pq.empty () pq.size () pq.push (item) pq.pop ()
优先队列的声明
1 2 3 4 5 priority_queue <int > i; priority_queue <double > d; priority_queue <int ,vector<int >,less<int > >q; priority_queue <int ,vector<int >,greater<int > > q;
结构体的优先队列
重写cmp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 struct node { int fir,sec; void Read () {scanf ("%d %d" ,&fir,&sec);} }input;struct cmp1 { bool operator () (const node &x,const node &y) const { return x.fir<y.fir; } };struct cmp3 { bool operator () (const node &x,const node &y) const { return x.fir+x.sec<y.fir+y.sec; } }; priority_queue<node,vector<node>,cmp1> q1; priority_queue<node,vector<node>,cmp3> q3; while (!q1.empty ()) printf ("(%d,%d) " ,q1.top ().fir,q1.top ().sec),q1.pop (); while (!q3.empty ()) printf ("(%d,%d) " ,q3.top ().fir,q3.top ().sec),q3.pop (); 【输入】1 2 2 1 6 9 9 6 -100 100 -500 20 4000 -3000 【输出】 cmp1: (4000 ,-3000 ) (9 ,6 ) (6 ,9 ) (2 ,1 ) (1 ,2 ) (-100 ,100 ) (-500 ,20 ) cmp3: (4000 ,-3000 ) (6 ,9 ) (9 ,6 ) (1 ,2 ) (2 ,1 ) (-100 ,100 ) (-500 ,20 )
stack
1 2 3 4 5 6 7 stack<int > s; s.push (item) s.top () s.pop () s.empty () s.size () s.emplace (item)
set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 s.size () s.empty () s.clear () s.insert (key) s.erase (pos/key) s.erase (3 ); auto it = s.find (4 );if (it != s.end ()){ s.erase (it); } s.count (key) s.find (key) ms.size () ms.empty () ms.clear () ms.insert (key) ms.erase (pos/key) ms.count (key) ms.find (key) for (int i=1 ;i<=n;i++) s.insert (gint ());class MyCompare { public : bool operator () (int v1, int v2) { return v1 > v2; } };int main () { set<int > s1 = {10 , 20 , 30 , 40 , 50 }; set<int , MyCompare> s2 = {10 , 20 , 30 , 40 , 50 }; return 0 ; }for (set<int >::iterator it = s1.begin (); it != s1.end (); ++it) { cout << *it << ' ' ; }for (auto it = mySet.begin (); it != mySet.end (); ++it) { cout << *it << ' ' ; }for (int it : mySet) { cout << it << ' ' ; }
pair
可以用来代替一些便捷的自定义struct。且pair自带小于号,可直接用于排序,第一关键字为第一维升序,第二关键字为第二维升序
1 2 3 pair<int ,int > p1; pair<int ,string> p2; pair<double ,int > p3;
map
构建⼀个映射关系复杂度为 $O(logn)$
1 2 3 4 5 map<T1,T2> mp; map<int ,int > mp1; map<string,int > mp2; map<int ,set<int > mp3; map<int ,vector<int >> mp4;
存图
邻接矩阵
存储稠密图
实现时需要注意重边与自环 。对于最短路问题,可以在重复的边中选择边权最小的一条保留。
Floyd 算法适合邻接矩阵
邻接表
存储稠密图
对于每个结点,使用一个 vector 保存与之相连的边。
vector实现无权邻接表
假设图中总共至多有 N 个结点,每条边不含边权。可以这样实现邻接表:
1 2 3 4 5 6 vector<int > g[N]; g[u].emplace_back (v); for (int i = 0 ; i < g[u].size (); i++) { int v = g[u][i]; }
实际上,可以使用语法糖简化遍历出边的实现,但是并不建议滥用 auto。
1 2 3 4 for (auto v : g[u]) { }
vector实现有权邻接表
对于具有边权 或是其他信息 的边,可以定义结构体以保存边的信息。
1 2 3 4 5 6 struct Edge { int to; int weight; } vector<Edge> g[N]; g[u].emplace_back ({v, w});
pair实现有权邻接表
两个元素的有序对 ⟨x, y⟩
可以使用 STL 的 pair 保存。
pair ⟨x, y⟩
之间的大小关系定义为:$⟨x1, y1⟩ < ⟨x2, y2⟩ ⇐⇒ x1 < x2 ∨ (x1 = x2 ∧ y1 < y2)$
第一个元素类型 T1,第二个元素类型 T2 的 pair:pair<T1, T2> p;
创建一个 pair:p = make_pair(x, y);
取 pair 的第一个元素:p.first
取 pair 的第二个元素:p.second
可以用 pair 实现邻接表。第一个元素保存边指向的点 ,第二个元素保存边权 :
1 2 3 4 5 6 7 vector<pair<int , int >> g[N]; g[u].emplace_back (make_pair (v, w));for (auto e : g[u]) { int v = e.first, w = e.second; }
char*和string
char* to string
1 2 char * name; string softwareName = name;
string to char*
1 2 3 4 5 6 7 char * strToChar (string strSend) { char * ConvertData; const int len2 = strSend.length (); ConvertData = new char [len2 + 1 ]; strcpy (ConvertData, strSend.c_str ()); return ConvertData; }
插入排序
1 2 3 4 5 6 7 8 9 void insertSort (keytype k[ ],int n) { keytype temp; for (int i=1 ; i<n; i++){ temp = k[i]; for (int j=i-1 ; j>=0 && temp<k[j]; j--) k[j+1 ] = k[j]; k[j+1 ] = temp; } }
归并排序 $O(n log n)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> #include <ctype.h> #include <string.h> #include <math.h> #include <stdlib.h> #define MAX 1000 int temp[MAX], ans[MAX] = {50 , 10 , 20 , 30 , 70 , 40 , 80 , 60 }; void Mergesort (int startId, int endId) ; void Merge (int startId, int midId, int endId) ; int main () { Mergesort(0 , 7 ); for (int i=0 ; i<8 ; i++) printf ("%d " , ans[i]); return 0 ; } void Mergesort (int startId, int endId) { int midId = startId + (endId - startId)/2 ; if (startId < endId){ Mergesort(startId, midId); Mergesort(midId+1 , endId); Merge(startId, midId, endId); } } void Merge (int startId, int midId, int endId) { int i=startId, j=midId+1 , k=startId; while (i <= midId && j<=endId){ if (ans[i] < ans[j]){ temp[k] = ans[i]; k++; i++; }else { temp[k] = ans[j]; k++; j++; } } while (i <= midId){ temp[k] = ans[i]; k++; i++; } while (j <= endId){ temp[k] = ans[j]; k++; j++; } for (i=startId; i<=endId; i++){ ans[i] = temp[i]; } }
逆序对计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> using namespace std; typedef long long ll; #define MAX (200000+5) int temp[MAX], a[MAX]; ll solve (int a[], int left, int right) ; int main () { int n; cin >> n; for (int i = 0 ; i < n; i++) cin >> a[i]; cout << solve (a, 0 , n - 1 ); return 0 ; } ll solve (int a[], int left, int right) { if (left == right) return 0 ; else { int mid = (right - left) / 2 + left; ll s1 = solve (a, left, mid); ll s2 = solve (a, mid + 1 , right); ll s3 = 0 ; int i = left, j = mid + 1 , k = 0 ; while (i <= mid && j <= right) { if (a[i] <= a[j]) { temp[left + k] = a[i]; k++; i++; } else { temp[left + k] = a[j]; s3 += (mid - i + 1 ); k++; j++; } } if (i <= mid) while (i <= mid) { temp[k + left] = a[i]; k++; i++; } else while (j <= right) { temp[k + left] = a[j]; k++; j++; } for (int l = left; l <= right; l++) a[l] = temp[l]; return s1 + s2 + s3; } }
多数问题
n个数组成一个数组,寻找是否有一个数的数量≥n/2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <stdio.h> const int N = 2000000 ; int a[N]; int majorityDC (int a[], int start, int end, int *result) { if (start == end) { *result = a[end]; return 1 ; }else { int m1, m2; majorityDC(a, start, (start + end) / 2 , &m1); majorityDC(a, (start + end) / 2 + 1 , end, &m2); int count1 = 0 , count2 = 0 ; for (int i = start; i <= end; i++) { if (a[i] == m1) { count1++; } if (a[i] == m2) { count2++; } } if (count1 > ((end - start + 1 ) / 2 )) { *result = m1; return 1 ; }else if (count2 > ((end - start + 1 ) / 2 )) { *result = m2; return 1 ; }else { return 0 ; } } } int main () { int n, resultDC; scanf ("%d" , &n); for (int i=0 ; i<n; i++){ scanf ("%d\n" , &a[i]); } if (majorityDC(a, 0 , n - 1 , &resultDC)){ printf ("%d" , resultDC); }else { printf ("Can not find the majority!" ); } return 0 ; }
维护堆的性质
堆的定义:
是一棵完全二叉树
每个节点的值都大于或等于其子节点的值,为最大堆;反之为最小堆。
堆的存储:一般用数组来表示堆,下标为i的结点的父结点下标为$\frac{i-1}2$;其左右子结点分别为 $2i + 1$、$2i + 2$(若数组编号从0开始)。下标为i的结点的父结点下标为$\frac{i-1}2$;其左右子结点分别为$2i$、$2i + 1$(若数组编号从1开始)
时间复杂度$O(lgn)$或对于树高h的节点来说,时间复杂度$O(h)$
c递归维护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void max_heapify (int arr[], int i, int n) { int l = 2 *i+1 , r = 2 *i+2 ; int largest = i; if (l <= n && arr[l]>arr[largest]){ largest = l; } if (r <= n && arr[r]>arr[largest]){ largest = r; } if (largest != i){ swap(&arr[i], &arr[largest]); max_heapify(arr, largest); } } void swap (int * a, int * b) { int temp = *b; *b = *a; *a = temp; }
c非递归维护
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void max_heapify (int arr[], int start, int end) { int dad = start; int son = dad * 2 + 1 ; while (son <= end) { if (son + 1 <= end && arr[son] < arr[son + 1 ]) son++; if (arr[dad] > arr[son]) return ; else { swap(&arr[dad], &arr[son]); dad = son; son = dad * 2 + 1 ; } } } void swap (int * a, int * b) { int temp = *b; *b = *a; *a = temp; }
建堆
时间复杂度$O(n)$
1 2 3 4 5 void build_max_heap (int arr[ ],int n) { int i; for (i=n/2 -1 ; i>=0 ; i--) max_heapify(arr, i, n); }
堆排序算法
时间复杂度$O(nlgn)$
c非递归维护最大堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void heapsort (int arr[], int n) { build_max_heap(arr, n); for (int i = n - 1 ; i > 0 ; i--) { swap(&arr[0 ], &arr[i]); max_heapify(arr, 0 , i - 1 ); } } void build_max_heap (int arr[ ],int n) { int i; for (i=n/2 -1 ; i>=0 ; i--) max_heapify(arr, i, n); } void max_heapify (int arr[], int i, int n) { int l = 2 *i+1 , r = 2 *i+2 ; int largest = i; if (l <= n && arr[l]>arr[i]){ largest = l; } if (r <= n && arr[r]>arr[largest]){ largest = r; } if (largest != i){ swap(&arr[i], &arr[largest]); max_heapify(arr, largest, n); } } void swap (int * a, int * b) { int temp = *b; *b = *a; *a = temp; }
c+cpp递归维护最大堆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> #include <stdlib.h> void swap (int * a, int * b) { int temp = *b; *b = *a; *a = temp; } void max_heapify (int arr[], int start, int end) { int dad = start; int son = dad * 2 + 1 ; while (son <= end) { if (son + 1 <= end && arr[son] < arr[son + 1 ]) son++; if (arr[dad] > arr[son]) return ; else { swap(&arr[dad], &arr[son]); dad = son; son = dad * 2 + 1 ; } } } void heap_sort (int arr[], int len) { int i; for (i = len / 2 - 1 ; i >= 0 ; i--) max_heapify(arr, i, len - 1 ); for (i = len - 1 ; i > 0 ; i--) { swap(&arr[0 ], &arr[i]); max_heapify(arr, 0 , i - 1 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <algorithm> using namespace std;void max_heapify (int arr[], int start, int end) { int dad = start; int son = dad * 2 + 1 ; while (son <= end) { if (son + 1 <= end && arr[son] < arr[son + 1 ]) son++; if (arr[dad] > arr[son]) return ; else { swap (arr[dad], arr[son]); dad = son; son = dad * 2 + 1 ; } } } void heap_sort (int arr[], int len) { for (int i = len / 2 - 1 ; i >= 0 ; i--) max_heapify (arr, i, len - 1 ); for (int i = len - 1 ; i > 0 ; i--) { swap (arr[0 ], arr[i]); max_heapify (arr, 0 , i - 1 ); } }
用cpp的algorithm建堆和堆排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <bits/stdc++.h> using namespace std; int main () { vector<int > a = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 }; make_heap (a.begin (), a.end ()); for (int i : a){ cout << i << " " ; } cout << endl; sort_heap (a.begin (), a.end ()); for (int i : a){ cout << i << " " ; } return 0 ; }
c优先队列
读最大元素 O(1)
1 2 3 int heap_maximum (int arr[]) { return arr[0 ]; }
移走最大元素 O(lgn)
1 2 3 4 5 6 7 8 9 10 11 int heap_extract_max (int arr[], int n) { if (n < 1 ){ printf ("HEAP UNDERFLOW" ); return -1 ; } int max = arr[0 ]; arr[0 ] = arr[n-1 ]; n--; max_heapify(arr, 0 , n); return max; }
增加某结点的值 O(lgn)
1 2 3 4 5 6 7 8 9 10 void heap_increase_key (int arr[], int i, int key) { if (key < arr[i]){ printf ("NEW KEY IS SMALLER THAN CURRENT KEY" ); } arr[i] = key; while (i>0 && arr[(i-1 )/2 ]<arr[i]){ swap(&a[i], &arr[(i-1 )/2 ]); i = (i-1 )/2 ; } }
插入一个新节点 O(lgn)
1 2 3 4 5 void max_heap_insert (int arr[], int key, int n) { n++; a[n-1 ] = key-1 ; heap_increase_key(arr, n-1 , key); }
快速排序
最好情况/平均情况:$O(nlgn)$
法1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> #define keytype int using namespace std ; void quickSort (keytype k[],int n) ; void quick (keytype k[ ],int left,int right) ; void swap (int *x, int *y) ; void quickSort (keytype k[],int n) { quick(k, 0 , n-1 ); } void quick (keytype k[ ],int left,int right) { int i, j; keytype pivot; if (left < right){ i = left; j = right+1 ; pivot = k[left]; while (1 ){ while (k[++i]<pivot && i!=right) { } while (k[--j]>pivot && j!=left) { } if (i < j) swap(&k[i], &k[j]); else break ; } swap(&k[left], &k[j]); quick(k, left, j-1 ); quick(k, j+1 , right); } } void swap (keytype *x, keytype *y) { keytype temp; temp = *x; *x = *y; *y = temp; } int main () { int a[100 ] = {2 ,3 ,4 ,5 ,1 ,123 ,1524 ,4235 ,345 ,34 ,67 ,324 }; quickSort(a, 12 ); for (int i=0 ; i<12 ; i++){ printf ("%d " , a[i]); } return 0 ; }
法2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <bits/stdc++.h> #define keytype int using namespace std ; void quickSort (keytype k[],int n) ; void qsort (keytype v[ ],int left, int right) ; void swap (keytype v[ ],int i, int j) ; void quickSort (keytype k[],int n) { qsort(k, 0 , n-1 ); } void qsort (keytype v[ ],int left, int right) { int i, last; if (left >= right) return ; swap(v, left, (left+right)/2 ); last = left; for (i=left+1 ; i<=right; i++) if (v[i] < v[left]) swap(v, ++last, i); swap(v, left, last); qsort(v, left, last); qsort(v, last+1 , right); } void swap (keytype v[ ],int i, int j) { keytype tmp; tmp = v[i]; v[i] = v[j]; v[j] = tmp; } int main () { int a[100 ] = {2 ,3 ,4 ,5 ,1 ,123 ,1524 ,4235 ,345 ,34 ,67 ,324 }; quickSort(a, 12 ); for (int i=0 ; i<12 ; i++){ printf ("%d " , a[i]); } return 0 ; }
快速排序的随机化版本
最坏情况:$O(n^2)$
期望运行时间:$O(nlgn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <bits/stdc++.h> #include <random> using namespace std; int randomized_partition (int a[], int p, int r) ; void randomized_quiksort (int a[], int p, int r) ; int partition (int a[], int p, int r) ; void randomized_quiksort (int a[], int p, int r) { if (p < r){ int q = randomized_partition (a, p, r); randomized_quiksort (a, p, q-1 ); randomized_quiksort (a, q+1 , r); } } int randomized_partition (int a[], int p, int r) { int i = rand ()%(r-p)+p; swap (a[r], a[i]); return partition (a, p, r); } int partition (int a[], int p, int r) { int x = a[r]; int i = p-1 ; for (int j=p; j<=r-1 ; j++){ if (a[j] <= x){ i++; swap (a[i], a[j]); } } swap (a[i+1 ], a[r]); return i+1 ; } int main () { int a[100 ] = {2 ,3 ,4 ,5 ,1 ,123 ,1524 ,4235 ,345 ,34 ,67 ,324 }; randomized_quiksort (a, 0 , 12 ); for (int i=0 ; i<12 ; i++){ printf ("%d " , a[i]); } return 0 ; }
最大子列和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> #include <algorithm> #define MAX 1000005 #define ll long long using namespace std; int a[MAX]; ll maxSubArray (int *array, int n) { ll Max = -0x3f3f3f3f3f3f3f3f ; ll sum = 0 ; for (int i = n - 1 ; i >= 0 ; i--) { if (sum < 0 ) sum = array[i]; else sum += array[i]; Max = max (sum, Max); } return Max; } int main () { int n; cin >> n; for (int i = 0 ; i < n; i++) cin >> a[i]; cout << maxSubArray (a, n); return 0 ; }
钢条切割
给定一段长度为 $n$ 英寸的钢条和一个价格表 $p_i(i=1,2,…,n)$ ,求切割钢条方案,使得销售收益 $r_n$ 最大。
带备忘的自顶向下法(top-down with memoization)
复杂度:$O(n^2)$
将钢条分为两部分,左边长度为 $i$ ,右边长度为 $n-i$ ,只对右边继续进行切割(递归求解),对左边不再进行切割。
这样,不做任何切割的方案就可以描述为:第一段的长度为 $n$ ,收益为 $p_n$ ,剩余部分长度为 $0$ ,对应的收益为 $r_n=0$ 。于是我们可以得到公式:$r_n = max_{1\ge i \ge n}(p_i + r_{n-i})$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> using namespace std ; #define MAX 100 #define MIN_VALUE -1 int max (int a, int b) { if (a > b){ return a; }else { return b; } } int MEMOIZED_CUT_ROD_AUX (int * p, int n, int * r) { int q; if (r[n] >= 0 ){ return r[n]; } if (n == 0 ){ q = 0 ; }else { q = MIN_VALUE; for (int i=1 ; i<=n; i++){ q = max(q, p[i]+MEMOIZED_CUT_ROD_AUX(p, n-i, r)); } } r[n] = q; return q; } int MEMOIZED_CUT_ROD (int * p, int n) { int r[MAX]; for (int i=0 ; i<=n; i++){ r[i] = MIN_VALUE; } return MEMOIZED_CUT_ROD_AUX(p, n, r); }int main () { int p[15 ] = {0 , 1 , 5 , 8 , 9 , 10 , 17 ,17 , 20 , 24 , 30 }; printf ("%d" , MEMOIZED_CUT_ROD(p, 14 )); return 0 ; }
给出最大收益及最优切割方案
下面的 EXTENDED_BOTTOM_UP_CUT_ROD 对长度为 $i$ 的钢条不仅计算最大收益值 $r_j$ ,还保存最优解对应的第一段钢条的切割长度 $S$ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <bits/stdc++.h> using namespace std; #define MAX 100 #define MIN_VALUE (-1) int s[MAX], r[MAX]; void EXTENDED_BOTTOM_UP_CUT_ROD (int *p, int n) { r[0 ] = 0 ; for (int j=1 ; j<=n; j++){ int q = MIN_VALUE; for (int i=1 ; i<=j; i++){ if (q < p[i]+r[j-i]){ q = p[i]+r[j-i]; s[j] = i; } } r[j] = q; } } void PRINT_CUT_ROD_SOLUTION (int *p,int n) { EXTENDED_BOTTOM_UP_CUT_ROD (p, n); printf ("%d\n" , r[n]); while (n > 0 ){ printf ("%d " , s[n]); n -= s[n]; } } int main () { int p[15 ] = {0 , 1 , 5 , 8 , 9 , 10 , 17 ,17 , 20 , 24 , 30 }; PRINT_CUT_ROD_SOLUTION (p, 14 ); return 0 ; }
矩阵链乘法
矩阵链乘法问题 :给定n个矩阵的链$<A_1,A_2,…,A_n>$,矩阵$A_i$是规模为 $p_{i-1}\times p_{i}(1\le i\le n)$,求完全括号化方案,使得计算乘积 $A_1A_2…A_n$ 所需标量乘法次数最少。
完全括号化 :它是单一矩阵,或者是两个完全括号化的矩阵乘积链的积,且已外加括号。
例如,如果矩阵链为$<A_1,A_2,A_3,A_4>$,则共有5种完全括号化的矩阵乘积链:$(A_1\ (A_{2}\ (A_{3}\ A_{4}))),\ (A_{1}\ ((A_{2}\ A_3)A_{4})),\ ((A_{1}\ A_2)(A_{3}\ A_{4})),$$\ ((A_{1}(A_{2}\ A_{3}))A_{4}),\ (((A_{1}\ A_2)A_{3}\ )A_{4}\ )$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <bits/stdc++.h> using namespace std ; #define MAX 100 long long p[MAX],m[MAX][MAX],s[MAX][MAX]; void MATRIX_CHAIN_ORDER (int n) { for (int i=1 ; i<=n; i++){ m[i][i] = 0 ; } for (int l=2 ; l<=n; l++){ for (int i=1 ; i<=n-l+1 ; i++){ int j = i+l-1 ; m[i][j] = 9223372036854775807 ; for (int k=i; k<=j-1 ; k++){ long long q = m[i][k]+m[k+1 ][j] + p[i-1 ]*p[k]*p[j]; if (q < m[i][j]){ m[i][j] = q; s[i][j] = k; } } } } } void PRINT_OPTIMAL_PARENS (long long i, long long j) { if (i == j){ printf ("A" ); }else { printf ("(" ); PRINT_OPTIMAL_PARENS(i, s[i][j]); PRINT_OPTIMAL_PARENS(s[i][j]+1 , j); printf (")" ); } } int main () { int n; scanf ("%d" , &n); for (int i=0 ; i<=n; i++){ scanf ("%lld" , &p[i]); } MATRIX_CHAIN_ORDER(n); printf ("%lld\n" , m[1 ][n]); PRINT_OPTIMAL_PARENS(1 ,n); return 0 ; }
最长公共子序列
子序列 :给定一个序列 $X = <x_1, x_2, …, x_m>$,另一个序列 $Z = <z_1,z_2,…,z_k>$ 满足如下条件时成为 $X$ 的子序列(subsequence),即存在一个严格递增的 $X$ 的下表序列 $<i_1,i_2,…,i_k>$,对所有 $j=1,2,…,k$,满足 $x_{i_1} = z_j$ 。
例如,$Z=<B,C,D,B>$ 是 $X=<A,B,C,B,D,A,B>$ 的子序列,对应的下标序列为 $<2,3,5,7>$ 。
公共子序列 :给定两个序列 $X$ 和 $Y$ ,如果 Z 既是 X 的子序列,也是 Y 的子序列,我们称它是 X 和 Y 的公共子序列(common subsequence)。
最长公共子序列问题 (longest-common-subsequence problem):给定两个序列 $X = <x_1,x_2,…,x_m>$ 和 $Y=<y_1,y_2,…,y_n>$,求 $X$ 和 $Y$ 长度最长的公共子序列。
复杂度:$O(n^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <stdio.h> #include <string.h> #define MAX 100 int c[MAX][MAX]; void LCS_LENGTH (char *x, char *y) { int xLen = strlen (x), yLen = strlen (y); for (int i=1 ; i<xLen; i++){ if (y[0 ] == x[i] || c[i-1 ][0 ] == 1 ){ c[i][0 ] = 1 ; } } for (int i=1 ; i<yLen; i++){ if (x[0 ] == y[i] || c[0 ][i-1 ] == 1 ){ c[0 ][i] = 1 ; } } for (int i=1 ; i<xLen; i++){ for (int j=1 ; j<yLen; j++){ if (x[i] == y[j]){ c[i][j] = c[i-1 ][j-1 ] + 1 ; }else if (c[i-1 ][j] >= c[i][j-1 ]){ c[i][j] = c[i-1 ][j]; }else { c[i][j] = c[i][j-1 ]; } } } } void PRINT_LCS (char *x, char *y, int i, int j) { if (i == -1 || j == -1 ){ return ; } if (x[i] == y[j]){ PRINT_LCS(x, y, i-1 , j-1 ); printf ("%c " , x[i]); }else if (c[i-1 ][j] >= c[i][j-1 ]){ PRINT_LCS(x, y, i-1 , j); }else { PRINT_LCS(x, y, i, j-1 ); } } int main () { char x[MAX] = "ABCBDAB" , y[MAX] = "BDCABA" ; LCS_LENGTH(x,y); PRINT_LCS(x, y, strlen (x)-1 , strlen (y)-1 ); return 0 ; }
最长公共子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int PRINT_LCS2 (char *s1, char *s2) { int max = 0 , start = 0 , len1 = strlen (s1), len2 = strlen (s2); for (int i=1 ; i<=len1; i++){ for (int j=1 ; j<=len2; j++){ if (s1[i-1 ] == s2[j-1 ]) dp[i][j] = dp[i-1 ][j-1 ] + 1 ; if (dp[i][j] > max){ max = dp[i][j]; start = i - max; } } } return max; }
最优二叉搜索树
最优二叉搜索树问题 :给定一个 $n$ 个不同关键字的已排序的序列 $K=<k_1,k_2,…,k_n>( k_1<k_2<…<k_n)$ ,我们希望用这些关键字构造一棵二叉搜索树。对每个关键字 $k$,都有一个概率$p_i$,表示其搜索频率。有些要搜索的值可能不在 $K$ 中,因此我们还有 $n+1个$ 伪关键字 $d_0,d_1,d_2,…,d+n$ 表示不在 $K$ 中的值。$d_0$ 表示所有小于 $k_1$ 的值,$d_n$ 表示所有大于 $k_n$ 的值,对 $i=1,2,…,n-1$,伪关键字 $d_i$ 表示所有在 $k_i$ 和 $k_{i+1}$ 之间的值。对每个伪关键字 $d_i$,也都有一个概率 $q_i$ 表示对应的搜索频率。每个关键字 $k_i$ 是一个内部结点,而每个伪关键字 $d_i$ 是一个叶结点。每次搜索要么成功(找到某个关键字 $k_i$)要么失败(找到某个为关键字 $d_i$ ),因此有如下公式:$\sum\limits^{n}{i=1}p {i}+\sum\limits^{n}{i=0}q {i} = 1$
最优二叉搜索树 :对于一个给定的概率集合,我们希望构造一棵期望搜索代价最小的二叉搜索树,我们称之为最优二叉搜索树
复杂度:$\Theta(n^{3})$
穷举法获得最优二叉搜索树的时间复杂度为 $\Omega (\frac{4^{n}}{n^{\frac{3}{2}}})$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <stdio.h> #define MAX (1000+10) #define MAXE 1000000000 double p[MAX], q[MAX], w[MAX][MAX], e[MAX][MAX]; int root[MAX][MAX], n = 5 ; void optimalBST (double *p,double *q,int n) ; void printRoot () ; void printOptimalBST (int i,int j,int r) ; int main () { scanf ("%d" , &n); for (int i=1 ; i<=n; i++){ scanf ("%lf" , &p[i]); } for (int i=0 ; i<=n; i++){ scanf ("%lf" , &q[i]); } optimalBST(p,q,n); printf ("%lf\n" , e[1 ][n]); printRoot(); printf ("最优二叉树结构:best structure\n" ); printOptimalBST(1 ,n,-1 ); return 0 ; } void optimalBST (double *p,double *q,int n) { for (int i = 1 ;i <= n + 1 ;++i){ w[i][i - 1 ] = q[i - 1 ]; e[i][i - 1 ] = q[i - 1 ]; } for (int len = 1 ;len <= n;++len){ for (int i = 1 ;i <= n - len + 1 ;++i){ int j = i + len - 1 ; e[i][j] = MAXE; w[i][j] = w[i][j - 1 ] + p[j] + q[j]; for (int k = i;k <= j;++k){ double temp = e[i][k - 1 ] + e[k + 1 ][j] + w[i][j]; if (temp < e[i][j]){ e[i][j] = temp; root[i][j] = k; } } } } } void printRoot () { printf ("各子树的根 roots\n" ); for (int i = 1 ;i <= n;++i){ for (int j = 1 ;j <= n;++j){ printf ("%d " , root[i][j]); } puts ("" ); } puts ("" ); } void printOptimalBST (int i,int j,int r) { int rootChild = root[i][j]; if (rootChild == root[1 ][n]){ printf ("k%d is root\n" , rootChild); printOptimalBST(i,rootChild - 1 ,rootChild); printOptimalBST(rootChild + 1 ,j,rootChild); return ; } if (j < i - 1 ){ return ; }else if (j == i - 1 ){ if (j < r){ printf ("d%d is k%d's left son\n" , j, r); }else { printf ("d%d is k%d's right son\n" , j, r); } return ; }else { if (rootChild < r){ printf ("k%d is k%d's left son\n" , rootChild, r); }else { printf ("k%d is k%d's right son\n" , rootChild, r); } } printOptimalBST(i,rootChild - 1 ,rootChild); printOptimalBST(rootChild + 1 ,j,rootChild); }
最小编辑距离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <string> #define MAX 2005 using namespace std; int ans; int dp[2 ][MAX]; int min3 (int a, int b, int c) { int m = a; if (b < m) m = b; if (c < m) return c; return m; } int minDistance (string word1, string word2) { int l1 = word1.length (); int l2 = word2.length (); for (int j = 0 ; j <= l2; j++) dp[0 ][j] = j; for (int i = 1 ; i <= l1; i++) { dp[1 ][0 ] = i; for (int j = 1 ; j <= l2; j++) if (word1[i - 1 ] == word2[j - 1 ]) dp[1 ][j] = dp[0 ][j - 1 ]; else { dp[1 ][j] = min3 (dp[0 ][j - 1 ], dp[0 ][j], dp[1 ][j - 1 ]) + 1 ; } for (int j = 0 ; j <= l2; j++) dp[0 ][j] = dp[1 ][j]; } return dp[0 ][l2]; } int main () { string a; string b; cin >> a >> b; cout << minDistance (a, b); printf ("\n%d\n" ,ans); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <iostream> #include <string> #define MAX 2005 using namespace std; int replace_count = 0 ; int delete_count = 0 ; int insert_count = 0 ; int dp[MAX][MAX] = {}; int min3 (int a, int b, int c) { int m = a; if (b < m) m = b; if (c < m) return c; return m; } void printOperations (string word1, string word2, int dp[MAX][MAX]) { int i = word1.length (); int j = word2.length (); while (i > 0 || j > 0 ) { if (i > 0 && j > 0 && word1[i - 1 ] == word2[j - 1 ]) { i--; j--; } else if (j > 0 && (i == 0 || dp[i][j] == dp[i][j - 1 ] + 1 )) { insert_count++; j--; } else if (i > 0 && (j == 0 || dp[i][j] == dp[i - 1 ][j] + 1 )) { delete_count++; i--; } else { replace_count++; i--; j--; } } } int minDistance (string word1, string word2) { int l1 = word1.length (); int l2 = word2.length (); for (int j = 0 ; j <= l2; j++) dp[0 ][j] = j; for (int i = 1 ; i <= l1; i++) { dp[i][0 ] = i; for (int j = 1 ; j <= l2; j++) { if (word1[i - 1 ] == word2[j - 1 ]) dp[i][j] = dp[i - 1 ][j - 1 ]; else dp[i][j] = min3 (dp[i - 1 ][j - 1 ], dp[i - 1 ][j], dp[i][j - 1 ]) + 1 ; } } printOperations (word1, word2, dp); return dp[l1][l2]; } int main () { string a; string b; cin >> a >> b; int distance = minDistance (a, b); cout << distance << endl; printf ("Replace: %d\n" , replace_count); printf ("Delete: %d\n" , delete_count); printf ("Insert: %d\n" , insert_count); return 0 ; }
最长单调子序列/LIS(Longest Increasing Subsequence)
$O(n^2)$ 做法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int LIS () { int ans=1 ; for (int i=1 ; i<=n; i++){ dp[i]=1 ; for (int j=1 ; j<i; j++){ if (a[i]>a[j]){ dp[i]=max (dp[i],dp[j]+1 ); } } ans=max (ans,dp[i]); } return ans; }int main () { while (cin>>n){ for (int i=1 ; i<=n; i++){ cin>>a[i]; } int ans=LIS (); cout<<ans<<"\n" ; } return 0 ; }
$O(nlgn)$ 做法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int lsrsa (const vector<int > &a) { vector<int > sa; for (auto x: a) { if (sa.empty () || x > sa.back ()) sa.push_back (x); else *lower_bound (sa.begin (), sa.end (), x) = x; } return (int ) sa.size (); } int lrsa (const vector<int > &a) { vector<int > sa; for (auto x: a) { if (sa.empty () || x >= sa.back ()) sa.push_back (x); else *upper_bound (sa.begin (), sa.end (), x) = x; } return (int ) sa.size (); }
活动选择问题
假定有一个 $n$ 个活动的集合 $S={a_{1}, a_{2}, …,a_{n}}$,这些活动使用同一个资源,而这个资源在某个时刻只能供一个活动使用,每个活动 $a_{i}$ 都有一个开始时间 $s_{i}$ 和一个结束时间 $f_{i}$ ,其中 $0\le s_{i}<f_{i}<∞$ ,如果被选中,任务 $a_i$ 发生在半开时间区间 $[s_i,f_i)$ 期间。如果两个活动时间不重叠,则称他们是兼容的 。在活动选择问题 中,我们希望选出一个最大兼容活动集 。假定活动已按结束时间的单调递增顺序排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <bits/stdc++.h> #define MAX 100 using namespace std ; struct thing { int startTime, endTime; }; struct thing things [MAX ], ans [MAX ]; int n; void Greedy_Activity_Selector () { printf ("0 " ); ans[0 ] = things[0 ]; int lastId=0 , ansNum = 1 ; for (int i=2 ; i<=n; i++){ if (things[i].startTime >= things[lastId].endTime){ ans[ansNum] = things[i]; ansNum++; lastId = i; printf ("%d " , i); } } printf ("\n%d" , ansNum); } int cmp (const void *a, const void *b) { struct thing *pa = (struct thing *)a; struct thing *pb = (struct thing *)b; if (pa->endTime < pb->endTime){ return -1 ; }else { if (pa->startTime < pb->startTime){ return -1 ; }else { return 1 ; } } } int main () { scanf ("%d" , &n); for (int i=0 ; i<n; i++){ scanf ("%d" , &things[i].startTime); } for (int i=0 ; i<n; i++){ scanf ("%d" , &things[i].endTime); } qsort(things, n, sizeof (struct thing), cmp); Greedy_Activity_Selector(); return 0 ; }
0-1背包
一个正在抢劫商店的小偷发现了 $n$ 个商品,第 $i$ 个商品价值 $v_i$ 美元,重 $w_i$ 磅,$v_{i}$ 和 $w_i$ 都是整数。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳 $W$ 磅重的商品,$W$ 是一个整数。他应该拿哪些商品呢?
二维dp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <bits/stdc++.h> using namespace std; const int N = 1010 ; const int MaxW = 10000 ; int n, W, v[N], w[N], f[N][MaxW]; int main () { cin >> n >> W; for (int i = 1 ; i <= n; ++i) cin >> v[i] >> w[i]; for (int i = 1 ; i <= n; i++){ for (int j = 0 ; j <= W; ++j){ f[i][j] = f[i - 1 ][j]; if (j >= w[i]) f[i][j] = max (f[i][j], f[i - 1 ][j - w[i]] + v[i]); } } cout << f[n][W] << endl; return 0 ; }
一维dp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <bits/stdc++.h> using namespace std; const int N = 1010 ; int n, W, v[N], w[N], f[N]; int main () { cin >> n >> W; for (int i = 1 ; i <= n; ++i) cin >> v[i] >> w[i]; for (int i = 1 ; i <= n; ++i){ for (int j = W; j >= w[i]; j--){ f[j] = max (f[j], f[j - w[i]] + v[i]); } } cout << f[W] << endl; return 0 ; }
01背包变种,一维dp
同样的 $w, v, W$,但背包容量 $W$ 极大,无法开数组 $f[0…W]$,求背包能装下的最大价值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <bits/stdc++.h> using namespace std; #define N 510 long long n, W, v[N], w[N], f[N*N], sumV; int main () { memset (f, 0x3f , sizeof (f)); f[0 ] = 0 ; cin >> n >> W; for (int i = 1 ; i <= n; ++i) { cin >> w[i] >> v[i]; sumV += v[i]; } for (int i = 1 ; i <= n; ++i){ for (int j=sumV; j>=v[i]; j--){ f[j] = min (f[j], f[j-v[i]]+w[i]); } } long long ans = 0 ; for (int j=0 ; j<=sumV; j++){ if (f[j] <= W){ ans = j; } } cout << ans << endl; return 0 ; }
完全背包
一个正在抢劫商店的小偷发现了 $n$ 种商品,第 $i$ 种商品价值 $v_i$ 美元,重 $w_i$ 磅,$v_{i}$ 和 $w_i$ 都是整数,每种商品都有无限个 。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳 $W$ 磅重的商品,$W$ 是一个整数。他应该拿哪些商品呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <bits/stdc++.h> using namespace std; const int N = 1010 ; int n, W, v[N], w[N], f[N]; int main () { cin >> n >> W; for (int i = 1 ; i <= n; ++i) cin >> v[i] >> w[i]; for (int i = 1 ; i <= n; ++i){ for (int j = v[i]; j <= W; j--){ f[j] = max (f[j], f[j - v[i]] + w[i]); } } cout << f[W] << endl; return 0 ; }
分组背包
物品被分成若干组,每组中的物品只能选择一个 。也就是说,从每组物品中,你只能选择一个或者不选,但不能选择多个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int MAX = 1005 ; struct { int cnt; ll ID[MAX]; } group[MAX]; ll dp[MAX]; ll val[MAX]; ll weight[MAX]; ll group_bag (int cap, int max_group) ; int main () { int n, W; cin >> W >> n; int a, b, k, max_group = 0 ; for (int i = 1 ; i <= n; i++) { cin >> a >> b >> k; weight[i] = a; val[i] = b; group[k].ID[group[k].cnt++] = i; max_group = max (max_group, k); } cout << group_bag (W, max_group); return 0 ; } ll group_bag (int W, int max_group) { for (int i = 0 ; i <= max_group; i++) for (ll j = W; j >= 0 ; j--) for (int k = 0 ; k < group[i].cnt; k++) if (j >= weight[group[i].ID[k]]) dp[j] = max (dp[j],dp[j - weight[group[i].ID[k]]] + val[group[i].ID[k]]); return dp[W]; }
多重背包
一个正在抢劫商店的小偷发现了 $n$ 种商品,第 $i$ 种商品价值 $v_i$ 美元,重 $w_i$ 磅,$v_{i}$ 和 $w_i$ 都是整数,每种商品有 $M_{i}$ 个 。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳 $W$ 磅重的商品,$W$ 是一个整数。他应该拿哪些商品呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std; const int MAXN=1e5 +10 ; int n,W; int v[MAXN],w[MAXN]; int f[MAXN]; int main () { cin >> n >> W; int cnt = 0 ; for (int i=1 ,a,b,s; i<=n; i++) { cin>> a >> b >> s; int k = 1 ; while (k <= s){ v[++cnt] = k*a; w[cnt] = k*b; s -= k; k *= 2 ; } if (s){ v[++cnt] = s*a; w[cnt] = s*b; } } for (int i=1 ; i<=cnt; i++) for (int j=W; j>=v[i]; j--) f[j] = max (f[j], f[j-v[i]]+w[i]); cout << f[W]; return 0 ; }
分数背包(部分背包)
一个正在抢劫商店的小偷发现了 $n$ 个商品,第 $i$ 个商品价值 $v_i$ 美元,重 $w_i$ 磅,$v_{i}$ 和 $w_i$ 都是整数,对每个商品,小偷可以拿走其一部分 ,而不是只能做出二元(0-1)选择。。这个小偷希望拿走价值尽量高的商品,但他的背包最多能容纳 $W$ 磅重的商品,$W$ 是一个整数。他应该拿哪些商品呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <cstdio> #include <cstdlib> struct coin { double w; double v; double rou; }; int cmp (const void *a, const void *b) { struct coin *pa = (struct coin *)a; struct coin *pb = (struct coin *)b; if (pa->rou > pb->rou){ return -1 ; }else { return 1 ; } } int main () { int n; double W; scanf ("%d%lf" , &n, &W); struct coin gold [110]= {}; for (int i=0 ; i<n; i++){ scanf ("%lf%lf" , &gold[i].w, &gold[i].v); gold[i].rou = gold[i].v / gold[i].w; } qsort(gold, n, sizeof (struct coin), cmp); double ans=0 ; int now=0 ; while (W >= 0 ){ if (now == n){ break ; } if (gold[now].w < W){ W -= gold[now].w; ans += gold[now].v; }else { ans += gold[now].v * (W/gold[now].w); W = 0 ; break ; } now++; } printf ("%.2lf" , ans); return 0 ; }
赫夫曼编码
考虑一种二进制字符编码 ,其中每个字符用一个唯一的二进制串表示,称为码字 。如果使用定长编码 ,需要用3位来表示6个字符:a=000,b=001,…,f=101。这种方法需要300000个二进制位来编码文件。是否有更好的编码方案呢?
时间复杂度 $O(nlgn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 100 typedef struct Node { char data; int frequency; struct Node * left ; struct Node * right ; } Node; Node* createNode (char data, int frequency) { Node* node = (Node*)malloc (sizeof (Node)); node->data = data; node->frequency = frequency; node->left = NULL ; node->right = NULL ; return node; } Node* buildHuffmanTree (char * inputText) { int charCount[256 ] = {}; int length = strlen (inputText); int i; for (i = 0 ; i < length; i++) { charCount[(int )inputText[i]]++; } Node* nodes[256 ]; int nodeCount = 0 ; for (i = 0 ; i < 256 ; i++) { if (charCount[i] > 0 ) { nodes[nodeCount] = createNode((char )i, charCount[i]); nodeCount++; } } while (nodeCount > 1 ) { int minFrequency1 = length + 1 ; int minFrequency2 = length + 1 ; int index1 = -1 ; int index2 = -1 ; for (i = 0 ; i < nodeCount; i++) { if (nodes[i]->frequency < minFrequency1) { minFrequency2 = minFrequency1; index2 = index1; minFrequency1 = nodes[i]->frequency; index1 = i; } else if (nodes[i]->frequency < minFrequency2) { minFrequency2 = nodes[i]->frequency; index2 = i; } } Node* parent = createNode('\0' , nodes[index1]->frequency + nodes[index2]->frequency); parent->left = nodes[index1]; parent->right = nodes[index2]; nodes[index2] = parent; nodes[index1] = nodes[nodeCount - 1 ]; nodeCount--; } return nodes[0 ]; } void printHuffmanCodes (Node* root, int code[], int top, int codeTable[][256 ], int codeLengths[]) { if (root->left) { code[top] = 0 ; printHuffmanCodes(root->left, code, top + 1 , codeTable, codeLengths); } if (root->right) { code[top] = 1 ; printHuffmanCodes(root->right, code, top + 1 , codeTable, codeLengths); } if (root->left == NULL && root->right == NULL ) { codeLengths[(int )root->data] = top; printf ("%c:" ,root->data); for (int i = 0 ; i < top; i++) { codeTable[(int )root->data][i] = code[i]; printf ("%d" ,code[i]); } printf ("\n" ); } } void encodeText (Node* root, char * inputText, char encodedText[], int codeTable[][256 ], int codeLengths[]) { int length = strlen (inputText); int i, j; for (i = 0 ; i < length; i++) { char character = inputText[i]; int length = codeLengths[(int )character]; for (j = 0 ; j < length; j++) { encodedText[strlen (encodedText)] = codeTable[(int )character][j] + '0' ; } } } void decodeText (Node* root, char * encodedText, char * decodedText) { int length = strlen (encodedText); int i = 0 ; while (i < length) { Node* current = root; while (current->left != NULL || current->right != NULL ) { if (encodedText[i] == '0' ) { current = current->left; } else if (encodedText[i] == '1' ) { current = current->right; } i++; } decodedText[strlen (decodedText)] = current->data; } decodedText[strlen (decodedText)] = '\0' ; } int main () { char inputText[MAX] = "" ; gets(inputText); Node* root = buildHuffmanTree(inputText); int code[256 ]; int top = 0 ; int codeTable[256 ][256 ] = {0 }; int codeLengths[256 ] = {0 }; printf ("Huffman Codes:\n" ); printHuffmanCodes(root, code, top, codeTable, codeLengths); printf ("\n" ); printf ("InputText:\n%s\n\n" ,inputText); char encodedText[1000 ] = "" ; encodeText(root, inputText, encodedText, codeTable, codeLengths); printf ("Encoded Text: \n%s\n\n" , encodedText); char decodedText[1000 ] = "" ; decodeText(root, encodedText, decodedText); printf ("Decoded Text: \n%s\n\n" , decodedText); return 0 ; }
链式前向星
1 2 3 4 5 6 7 8 9 10 11 12 13 struct Edge { int to, w, next; } e[E_MAX]; int cnt_E = 0 ; int head[V_MAX]; void intList (int n) { memset (head, -1 , sizeof (head)); } void addEdge (int x, int y, int w) { e[cnt_E].to = y; e[cnt_E].next = head[x]; head[x] = cnt_E++; }
BFS广度优先搜索
邻接矩阵
时间复杂度 $O(n^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <bits/stdc++.h> #define MaxV (10000+10) using namespace std; int G[MaxV][MaxV]; bool visited[MaxV]; int last[MaxV], d[MaxV]; void BFS (int i, int n) ; void printPath (int s, int v) ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ int u, v; scanf ("%d%d" , &u, &v); G[u][v] = 1 ; } BFS (1 , n); for (int i=2 ; i<=n; i++){ if (!visited[i]) BFS (i,n); } puts ("" ); printPath (1 , 4 ); puts ("" ); printf ("%d" , d[4 ]); return 0 ; } void BFS (int i, int n) { queue<int > Q; Q.push (i); visited[i] = true ; printf ("v%d->" , i); while (!Q.empty ()){ int k = Q.front (); Q.pop (); for (int j=1 ; j<=n; j++){ if (G[k][j] == 1 && !visited[j]){ Q.push (j); visited[j] = true ; printf ("v%d->" , j); d[j] = d[k]+1 ; last[j] = k; } } } }void printPath (int s, int v) { if (v == s){ printf ("%d " , s); }else if (last[v] == 0 ){ printf ("No path from %d to %d exists" , s, v); }else { printPath (s, last[v]); printf ("%d " , v); } }
邻接表
时间复杂度 $O(n+e)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 #include <bits/stdc++.h> #define MaxV (10000+10) using namespace std; typedef struct edge { int adjvex; int weight; struct edge *next; }ELink; typedef struct ver { int vertex; ELink* link; }VLink; VLink G[MaxV]; bool visited[MaxV]; int last[MaxV], d[MaxV]; void BFS (int i, int n) ; void printPath (int s, int v) ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ int u, v; scanf ("%d%d" , &u, &v); ELink* e = (ELink*) malloc (sizeof (ELink)); e->adjvex = v, e->weight = 0 , e->next = nullptr ; if (G[u].link == nullptr ){ G[u].link = e; }else { e->next = G[u].link; G[u].link = e; } } BFS (1 , n); for (int i=2 ;i<=n;i++){ if (!visited[i]) BFS (i,n); } puts ("" ); printPath (1 , 4 ); puts ("" ); printf ("%d" , d[4 ]); return 0 ; } void BFS (int i, int n) { queue<int > Q; ELink* p; Q.push (i); visited[i] = true ; printf ("v%d->" , i); while (!Q.empty ()){ int k = Q.front (); Q.pop (); p = G[k].link; while (p != nullptr ){ int j = p->adjvex; if (!visited[j]){ printf ("v%d->" , j); visited[j] = true ; Q.push (j); d[j] = d[k]+1 ; last[j] = k; } p = p->next; } } } void printPath (int s, int v) { if (v == s){ printf ("%d " , s); }else if (last[v] == 0 ){ printf ("No path from %d to %d exists" , s, v); }else { printPath (s, last[v]); printf ("%d " , v); } }
DFS深度优先搜索
邻接矩阵
时间复杂度 $O(n^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <bits/stdc++.h> #define MaxV (10000+10) using namespace std; int G[MaxV][MaxV]; bool visited[MaxV]; int last[MaxV], d[MaxV]; void DFS (int i, int n) ; void printPath (int s, int v) ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ int u, v; scanf ("%d%d" , &u, &v); G[u][v] = 1 ; } DFS (1 , n); for (int i=2 ;i<=n;i++){ if (!visited[i]) DFS (i,n); } puts ("" ); printPath (1 , 4 ); puts ("" ); printf ("%d" , d[4 ]); return 0 ; } void DFS (int i, int n) { printf ("v%d->" , i); visited[i] = true ; for (int j=1 ; j<=n; j++){ if (G[i][j] == 1 && !visited[j]){ last[j] = i; d[j] = d[i]+1 ; DFS (j, n); } } } void printPath (int s, int v) { if (v == s){ printf ("%d " , s); }else if (last[v] == 0 ){ printf ("No path from %d to %d exists" , s, v); }else { printPath (s, last[v]); printf ("%d " , v); } }
邻接表
时间复杂度 $O(n+e)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <bits/stdc++.h> #define MaxV (10000+10) using namespace std; typedef struct edge { int adjvex; int weight; struct edge *next; }ELink; typedef struct ver { int vertex; ELink* link; }VLink; VLink G[MaxV]; bool visited[MaxV]; int last[MaxV], d[MaxV]; void DFS (int i, int n) ; void printPath (int s, int v) ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ int u, v; scanf ("%d%d" , &u, &v); ELink* e = (ELink*) malloc (sizeof (ELink)); e->adjvex = v, e->weight = 0 , e->next = nullptr ; if (G[u].link == nullptr ){ G[u].link = e; }else { e->next = G[u].link; G[u].link = e; } } DFS (1 , n); for (int i=2 ;i<=n;i++){ if (!visited[i]) DFS (i,n); } puts ("" ); printPath (1 , 4 ); puts ("" ); printf ("%d" , d[4 ]); return 0 ; } void DFS (int i, int n) { ELink* p; printf ("v%d->" , i); visited[i] = true ; p = G[i].link; while (p != NULL ){ int j = p->adjvex; if (!visited[j]){ last[j] = i; d[j] = d[i]+1 ; DFS (j, n); } p = p->next; } } void printPath (int s, int v) { if (v == s){ printf ("%d " , s); }else if (last[v] == 0 ){ printf ("No path from %d to %d exists" , s, v); }else { printPath (s, last[v]); printf ("%d " , v); } }
拓扑排序
拓扑排序:对于有向无环图 $G$ 来说,如果图G包含边 $(u,v)$,则结点 $u$ 在拓扑排序中处于结点 $v$ 的前面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> using namespace std; #define MAX_VERTICES 10010 unordered_map<int , vector<int >>graph; struct Edge { int from, to, next; } e[MAX_VERTICES]; int cnt; int main () { int t; scanf ("%d" , &t); while (t--){ int head[MAX_VERTICES] = {}; int id[MAX_VERTICES]={}; memset (head, -1 , sizeof (head)); graph.clear (); int n, m; cnt = 1 ; scanf ("%d%d" , &n, &m); for (int i = 1 ; i <= m; i++) { int from, to; scanf ("%d%d" , &from, &to); e[cnt].from = from; e[cnt].to = to; e[cnt].next = head[from]; id[to]++; head[from] = cnt++; graph[from].push_back (to); } priority_queue<int > q; for (int i=1 ; i<=n; i++) { if (id[i] == 0 ) q.push (i); } vector<int > ans; while (!q.empty ()) { int x = q.top (); q.pop (); ans.push_back (x); int edge = head[x]; while (edge != -1 ) { id[e[edge].to]--; if (id[e[edge].to] == 0 ) q.push (e[edge].to); edge = e[edge].next; } } for (int an : ans){ printf ("%d " , an); } } return 0 ; }
单源最短路径
1 Bellman-Ford算法
权重可以为负 ,可以有回路 。时间复杂度$O(VE)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> #include <cstdio> using namespace std; #define MAX 0x3f3f3f3f #define N 1010 int nodenum, edgenum, original; typedef struct Edge { int u, v; int cost; }Edge; Edge edge[N]; int dis[N], pre[N]; bool Bellman_Ford () ; void print_path (int root) ; int main () { scanf ("%d%d%d" , &nodenum, &edgenum, &original); pre[original] = original; for (int i = 1 ; i <= edgenum; ++i){ scanf ("%d%d%d" , &edge[i].u, &edge[i].v, &edge[i].cost); } if (Bellman_Ford ()) for (int i = 1 ; i <= nodenum; ++i){ printf ("%d\n" , dis[i]); printf ("Path:" ); print_path (i); } else printf ("have negative circle\n" ); return 0 ; } bool Bellman_Ford () { for (int i = 1 ; i <= nodenum; ++i) dis[i] = (i == original ? 0 : MAX); for (int i = 1 ; i <= nodenum - 1 ; ++i) for (int j = 1 ; j <= edgenum; ++j) if (dis[edge[j].v] > dis[edge[j].u] + edge[j].cost){ dis[edge[j].v] = dis[edge[j].u] + edge[j].cost; pre[edge[j].v] = edge[j].u; } bool flag = true ; for (int i = 1 ; i <= edgenum; ++i) if (dis[edge[i].v] > dis[edge[i].u] + edge[i].cost){ flag = false ; break ; } return flag; } void print_path (int root) { while (root != pre[root]){ printf ("%d-->" , root); root = pre[root]; } if (root == pre[root]) printf ("%d\n" , root); }
2 有向无环图中的单源最短路径问题
权重可以为负 ,不能有回路 。时间复杂度$O(V+E)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <bits/stdc++.h> using namespace std; #define MAX_VERTICES 10010 #define MAX_EDGE 10010 typedef struct edge { long long adjvex; long long weight; struct edge *next; }ELink; typedef struct ver { long long vertex, id, d, last; ELink* link; }VLink; VLink G[MAX_VERTICES]; void InitializeSingleSource (long long n, long long s) ; void Relax (long long u, long long v, ELink* edge) ; int main () { int t; scanf ("%d" , &t); while (t--){ int n, m, s; scanf ("%d%d%d" , &n, &m, &s); for (int i=0 ; i<m; i++){ long long u, v, w; scanf ("%lld%lld%lld" , &u, &v, &w); ELink* e = (ELink*) malloc (sizeof (ELink)); e->adjvex = v, e->weight = w, e->next = nullptr ; if (G[u].link == nullptr ){ G[u].link = e; }else { e->next = G[u].link; G[u].link = e; } G[v].id++; } queue<long long > q; for (int i = 1 ; i <= n; i++) { if (G[i].id == 0 ) q.push (i); } vector<long long > ans; while (!q.empty ()) { long long x = q.front (); q.pop (); ans.push_back (x); ELink* edge = G[x].link; while (edge != nullptr ) { G[edge->adjvex].id--; if (G[edge->adjvex].id == 0 ) q.push (edge->adjvex); edge = edge->next; } } InitializeSingleSource (n, s); for (long long an: ans){ VLink now = G[an]; ELink* temp = now.link; while (temp != nullptr ) { Relax (an, temp->adjvex, temp); temp = temp->next; } } for (long long an: ans){ printf ("%lld:%lld " , an, G[an].d); } } return 0 ; } void InitializeSingleSource (long long n, long long s) { for (int i=1 ; i<=n; i++){ G[i].d = LONG_LONG_MAX; } G[s].d = 0 ; } void Relax (long long u, long long v, ELink* edge) { if (G[v].d > G[u].d+edge->weight){ G[v].d = G[u].d+edge->weight; G[v].last = u; } }
3 Dijkstra算法
非负 权重的图,可以有回路 。时间复杂度$O((V+E)*logV)$
标准版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <vector> #include <queue> #define MaxV 2005 using namespace std; struct Edge { int to; int weight; Edge (int t, int w) :to (t), weight (w) {} }; vector<int >dijkstra (const vector<vector<Edge>>& graph, int src) { vector<int >dis (MaxV, INT_MAX); vector<bool >vis (MaxV, false ); priority_queue<pair<int ,int >,vector<pair<int ,int > >,greater<pair<int ,int > > >pq; dis[src] = 0 ; pq.push ({0 ,src}); while (!pq.empty ()) { int v = pq.top ().second; pq.pop (); if (vis[v]) continue ; vis[v] = true ; for (const auto &edge: graph[v]) { int t = edge.to; int w = edge.weight; if (!vis[t] && w+dis[v]<dis[t]) { dis[t] = w + dis[v]; pq.push ({ dis[t],t }); } } } return dis; } int main () { int n, m, source; scanf ("%d%d%d" , &n, &m, &source); vector<vector<Edge>>graph (MaxV); for (int i=0 ; i<m; i++){ int u, v, w; scanf ("%d%d%d" , &u, &v, &w); graph[u].push_back (Edge (v,w)); } vector<int >shortest_path = dijkstra (graph, source); cout << shortest_path[3 ]; return 0 ; }
所有结点对的最短路径问题
floyd算法
空间复杂度 $O(V^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <stdio.h> #include <limits.h> #define V 310 long long dist[V][V], graph[V][V], Path[V][V]; void floydWarshall () ; void PrintPath (long long u, long long v) ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ long long u, v, w; scanf ("%lld%lld%lld" , &u, &v, &w); if ((graph[u][v]!=0 && w<graph[u][v]) || graph[u][v] == 0 ){ graph[u][v] = w; } } floydWarshall (); int q; scanf ("%d" , &q); while (q--){ int u, v; scanf ("%d%d" , &u, &v); if (u == v){ printf ("%d->%d: 0\n" , u, v); }else if (dist[u][v] == LONG_LONG_MAX){ printf ("%d->%d: -1\n" , u, v); }else { printf ("%d->%d: %lld\n" , u, v, dist[u][v]); PrintPath (u, v); } } return 0 ; } void floydWarshall () { for (int i = 0 ; i < V; i++) for (int j = 0 ; j < V; j++){ if (graph[i][j] != 0 ){ dist[i][j] = graph[i][j]; }else { dist[i][j] = LONG_LONG_MAX; } if (dist[i][j] < LONG_LONG_MAX && i != j){ Path[i][j] = j; }else { Path[i][j] = -1 ; } } for (int k = 0 ; k < V; k++) { for (int i = 0 ; i < V; i++) { for (int j = 0 ; j < V; j++) { if (dist[i][k] != LONG_LONG_MAX && dist[k][j] != LONG_LONG_MAX && dist[i][k] + dist[k][j] < dist[i][j]) { dist[i][j] = dist[i][k] + dist[k][j]; Path[i][j] = k; } } } } } void PrintPath (long long u, long long v) { printf ("%lld " , u); while (Path[u][v] != v){ printf ("%lld " , Path[u][v]); u = Path[u][v]; } printf ("%lld\n" , v); }
有向图的传递闭包
传递闭包 :$G*=(V,E*)$ ,其中 $E*={(i,j)|如果图G中包含一条从结点i到结点j的路径}$
用于解决问题:给定有向图,判断对于所有结点对i和j,图G是否包含一条从结点i到结点j的路径
时间复杂度:$O(n^3)$
法1:floyd算法每条边权重赋1
法2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <cstdio> #define V 310 long long dist[V][V]; void floydWarshall () ; int main () { int n, m; scanf ("%d%d" , &n, &m); for (int i=0 ; i<m; i++){ long long u, v, w; scanf ("%lld%lld%lld" , &u, &v, &w); dist[u][v] = 1 ; } floydWarshall (); int q; scanf ("%d" , &q); while (q--){ int u, v; scanf ("%d%d" , &u, &v); if (dist[u][v]){ printf ("%d->%d: 1\n" , u, v); }else { printf ("%d->%d: 0\n" , u, v); } } return 0 ; } void floydWarshall () { for (int i = 0 ; i < V; i++) dist[i][i] = 1 ; for (int k = 0 ; k < V; k++) { for (int i = 0 ; i < V; i++) { for (int j = 0 ; j < V; j++) { dist[i][j] = dist[i][j] | (dist[i][k] & dist[k][j]); } } } }
经过固定点的最短路径
最大流
Edmonds-Karp算法
时间复杂度 $O(VE^{2})$,其中 $V$ 为点的总数,$E$ 为边的总数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <bits/stdc++.h> using namespace std; const int N=210 ;const int INF=0x7FFFFFFF ; int n,m; int map0[N][N]; int pi[N]; int flow_in[N]; int start,end0; queue<int > q; int bfs () ; int Edmonds_Karp () ; int main () { int i,u,v,cost; while (scanf ("%d%d" ,&n,&m)!=EOF){ memset (map0,0 ,sizeof (map0)); for (i=0 ;i<n;i++){ scanf ("%d%d%d" ,&u,&v,&cost); map0[u][v]+=cost; } start=1 ,end0=m; printf ("%d\n" ,Edmonds_Karp ()); } return 0 ; } int bfs () { int i,t; while (!q.empty ()) q.pop (); memset (pi,-1 ,sizeof (pi)); pi[start]=0 ; flow_in[start]=INF; q.push (start); while (!q.empty ()){ t=q.front (); q.pop (); if (t==end0) break ; for (i=1 ;i<=m;i++){ if (pi[i]==-1 && map0[t][i]){ flow_in[i] = min (flow_in[t], map0[t][i]); q.push (i); pi[i]=t; } } } if (pi[end0]==-1 ) return -1 ; else return flow_in[m]; } int Edmonds_Karp () { int max_flow_in=0 ; int cf_p; while ((cf_p=bfs ())!=-1 ){ max_flow_in+=cf_p; int now=end0; while (now!=start){ int pre=pi[now]; map0[pre][now]-=cf_p; map0[now][pre]+=cf_p; now=pre; } } return max_flow_in; }
Dinic算法
时间复杂度 $O(V^{2}E)$,其中 $V$ 为点的总数,$E$ 为边的总数
第一行一个正整数 T(1≤T≤10),表示数据组数。
对于每组数据,第一行四个正整数 n,m,s,t(1≤n≤100,1≤m≤5×10^3,1≤s,t≤n),n个点,m条边,计算从s到t的最大流。
接下来 m 行,每行三个正整数 ui,vi,wi(1≤ui,vi≤n,0≤wi<2^31),表示第 i 条有向边 ui→vi 的最大容量为 wi。
图中有可能存在重边和自环 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <algorithm> #include <cstring> #include <iostream> #include <queue> using namespace std; typedef long long ll; const int V_MAX = 205 ; const int E_MAX = 5005 ; const ll LL_INF = 0x3f3f3f3f3f3f3f3f ; ll max_stream = 0 ; int cnt_E = 0 ; int n, m, s, t; struct Edge { int to; int nxt; ll val; } e[E_MAX * 2 ]; int head[V_MAX]; int depth[V_MAX]; void addEdge (int x, int y, int w) ; void read () ; bool bfs () ; ll Dinic () ; int main () { int T; cin >> T; while (T--){ cin >> n >> m >> s >> t; cnt_E = 0 , max_stream = 0 ; fill (head + 1 , head + 1 + n, -1 ); read (); cout << Dinic () << '\n' ; } return 0 ; } void addEdge (int x, int y, int w) { e[cnt_E].to = y; e[cnt_E].val = w; e[cnt_E].nxt = head[x]; head[x] = cnt_E++; } void read () { int u, v, w; for (int i = 0 ; i < m; i++) { cin >> u >> v >> w; addEdge (u, v, w); addEdge (v, u, 0 ); } } bool bfs () { memset (depth, 0 , sizeof (depth)); depth[s] = 1 ; queue<int > q; q.push (s); while (!q.empty ()) { int u = q.front (); q.pop (); for (int i = head[u]; i > -1 ; i = e[i].nxt) { int v = e[i].to; if (e[i].val && !depth[v]) { depth[v] = depth[u] + 1 ; q.push (v); } } } if (depth[t] != 0 ) return true ; return false ; } ll dfs (int pos, ll in) { if (pos == t) return in; ll out = 0 ; for (int u = head[pos]; u > -1 && in; u = e[u].nxt) { int v = e[u].to; if (e[u].val && depth[v] == depth[pos] + 1 ) { ll res = dfs (v, min (e[u].val, in)); e[u].val -= res; e[u ^ 1 ].val += res; in -= res; out += res; } } if (out == 0 ) depth[pos] = 0 ; return out; } ll Dinic () { while (bfs ()) max_stream += dfs (s, LL_INF); return max_stream; }
最大二分匹配
Dinic最小割/最大流算法
时间复杂度 $O(VE)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 #include <iostream> #include <algorithm> #include <cstring> #include <queue> using namespace std; const int N = 1e3 +10 , M = 5e5 +1e4 +10 , INT = 0x3f3f3f3f ; int e[M], ne[M], f[M], h[N], idx; int cur[N], d[N]; int n, m, eNum, S, T; void add (int a, int b, int c) ; int dinic () ; bool bfs () ; int dfs (int u, int lim) ; int main () { scanf ("%d%d%d" , &n, &m, &eNum); S = 0 , T = n + m + 1 ; memset (h, -1 , sizeof h); for (int i = 1 ; i <= n; i++) add (S, i, 1 ); for (int i = n + 1 ; i <= n+m; i++) add (i, T, 1 ); for (int i = 1 ; i <= eNum; i++){ int a, b; scanf ("%d%d" , &a, &b); add (a, b+n, 1 ); } cout << dinic () << "\n" ; for (int i=0 ;i<idx;i+=2 ) { if (e[i]>n && e[i]<=n+m && !f[i]) { printf ("%d %d\n" ,e[i^1 ],e[i]-n); } } return 0 ; } void add (int a, int b, int c) { e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++; e[idx] = a, f[idx] = 0 , ne[idx] = h[b], h[b] = idx++; } int dinic () { int r = 0 , flow; while (bfs ()) while (flow = dfs (S, INT)) r += flow; return r; } bool bfs () { queue<int > q; q.push (S); memset (d, -1 , sizeof d); d[S] = 0 , cur[S] = h[S]; while (q.size ()){ int t = q.front (); q.pop (); for (int i = h[t]; ~i; i = ne[i]){ int ver = e[i]; if (d[ver] == -1 && f[i]){ d[ver] = d[t] + 1 ; cur[ver] = h[ver]; if (ver == T) return true ; q.push (ver); } } } return false ; } int dfs (int u, int lim) { if (u == T) return lim; int flow = 0 ; for (int i = cur[u]; ~i && flow < lim; i = ne[i]){ int ver = e[i]; cur[u] = i; if (d[ver] == d[u] + 1 && f[i]){ int t = dfs (ver, min (f[i], lim - flow)); if (!t) d[ver] = -1 ; f[i] -= t, f[i^1 ] += t, flow += t; } } return flow; }
匈牙利算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <bits/stdc++.h> using namespace std; const int N=555 ; vector<int > G[N]; int match[N],vis[N]; bool used[N][N]; bool hungary (int p,int op) ; int main () { int n,m,e,a,b; scanf ("%d%d%d" ,&n,&m,&e); while (e--){ scanf ("%d%d" ,&a,&b); if (used[a][b]) continue ; used[a][b]=true ; G[a].push_back (b); } int ans=0 ; for (int i=1 ;i<=n;i++) if (hungary (i,i)) ans++; printf ("%d\n" ,ans); return 0 ; } bool hungary (int p,int op) { if (vis[p]==op) return false ; vis[p]=op; for (int i:G[p]){ if (!match[i]||hungary (match[i],op)){ match[i]=p; return true ; } } return false ; }
最小生成树
prim-朴素版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 2005 ,M = 5005 ,INF = 0x3f3f3f3f ; int n,m; int g[N][N]; int dist[N]; bool used[N];int prim () ; int main () { scanf ("%d%d" ,&n,&m); memset (g,0x3f ,sizeof (g)); for (int i = 0 ;i < m; i++){ int u,v,w; scanf ("%d%d%d" ,&u,&v,&w); g[u][v] = g[v][u] = min (g[u][v],w); } int r = prim (); printf ("%d" ,r); return 0 ; } int prim () { memset (dist,0x3f ,sizeof dist); int res = 0 ; for (int i = 0 ;i < n;i++){ int t = -1 ; for (int j = 1 ;j <= n; j++){ if ((!used[j]) && (t == -1 || dist[t] > dist[j])) t = j; } used[t] = true ; if (i && dist[t] == INF) return INF; if (i)res += dist[t]; for (int j = 1 ;j <= n;j++){ if (!used[j]) dist[j] = min (dist[j],g[t][j]); } } return res; }
prim-堆优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std; #define INF 2147483647 #define N (100000+10) typedef pair<int ,int >pii; struct cmp { bool operator () (pii &a,pii &b) { return a.second>b.second; } }; vector<pii>e[N]; int d[N],vis[N],cnt,sum,n,m; priority_queue <pii,vector<pii>,cmp > q; void add_edge (int x, int y, int z) ; void init (int n) ; void prim () ; int main () { scanf ("%d%d" ,&n,&m); init (n); for (int i=1 ; i<=m; i++){ int x,y,z; scanf ("%d%d%d" ,&x,&y,&z); add_edge (x,y,z); } prim (); if (cnt==n) printf ("%d" ,sum); else printf ("orz" ); } void add_edge (int x, int y, int z) { e[x].push_back (make_pair (y, z)); e[y].push_back (make_pair (x, z)); } void init (int n) { for (int i = 1 ; i <= n; i++) e[i].clear (); for (int i = 1 ; i <= n; i++) d[i] = INF; } void prim () { d[1 ]=0 ; q.push (make_pair (1 ,0 )); while (!q.empty () && cnt<n){ int now=q.top ().first; int dis=q.top ().second; q.pop (); if (vis[now]) continue ; cnt++; sum += dis; vis[now] = 1 ; for (int i=0 ; i<e[now].size (); i++){ int v=e[now][i].first; if (d[v]>e[now][i].second){ d[v]=e[now][i].second; q.push (make_pair (v,d[v])); } } } }
kruskal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <algorithm> #include <iostream> #define V_MAX 300005 #define E_MAX 500005 using namespace std; typedef long long ll; struct Edge { int x, y, w; bool operator <(const Edge &b) const { return w < b.w; } } e[E_MAX]; int v[V_MAX]; int Find (int x) ; bool isUnion (int x, int y) ; void Union (int x, int y) ; void makeSet (int n) ; int main () { int n, m; cin >> n >> m; makeSet (n); for (int i = 0 ; i < m; i++) cin >> e[i].x >> e[i].y >> e[i].w; sort (e, e + m); int cnt = 0 ; ll sum = 0 ; for (int i = 0 ; cnt < n - 1 ; i++){ if (isUnion (e[i].x, e[i].y)) continue ; cnt++; sum += e[i].w; Union (e[i].x, e[i].y); } cout << sum; return 0 ; } void makeSet (int n) { for (int i = 1 ; i <= n; i++) v[i] = i; } int Find (int x) { if (v[x] == x) return x; return v[x] = Find (v[x]); } bool isUnion (int x, int y) { return Find (x) == Find (y); } void Union (int x, int y) { v[Find (y)] = Find (x); }
傅里叶变换
最高次数为n,次数界可以为n, n+1, n+2, 2n
对于次数界为n的多项式 $A(x) = \sum\limits_{j=0}^{n-1}a_{j}x^{j}$ ,dft求的是 $y_{k} = A(\omega_{n}^{k})=\sum\limits_{j=0}^{n-1}a_{j}\omega_{n}^{kj}$ 即 $y = DFT_{n}(a)$ 其中 $ω=cos\frac{2π}{2^{n}}+i\ sin\frac{2π}{2^{n}}$
多项式乘法-普通版
时间复杂度 $\Theta(nlgn)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <iostream> #include <vector> #include <cmath> const double Pi = acos (-1 ); const int MAX = 4000005 ; using namespace std; typedef long long ll; struct Complex { double x, y; Complex operator +(const Complex &b) const { return {x + b.x, y + b.y}; } Complex operator -(const Complex &b) const { return {x - b.x, y - b.y}; } Complex operator *(const Complex &b) const { return {x * b.x - y * b.y, x * b.y + y * b.x}; } } f[MAX], p[MAX], sav[MAX]; void dft (Complex *f, int len) ; void idft (Complex *f, int len) ; int main () { int n, m; cin >> n >> m; for (int i = 0 ; i <= n; i++) cin >> f[i].x; for (int i = 0 ; i <= m; i++) cin >> p[i].x; for (m += n, n = 1 ; n <= m; n <<= 1 ); dft (f, n); dft (p, n); for (int i = 0 ; i < n; i++) f[i] = f[i] * p[i]; idft (f, n); for (int i = 0 ; i <= m; i++) cout << (int ) (f[i].x / n + 0.49 ) << " " ; return 0 ; } void dft (Complex *f, int len) { if (len == 1 ) return ; Complex *fl = f, *fr = f + len / 2 ; for (int k = 0 ; k < len; k++) sav[k] = f[k]; for (int k = 0 ; k < len / 2 ; k++) { fl[k] = sav[k << 1 ]; fr[k] = sav[k << 1 | 1 ]; } dft (fl, len / 2 ); dft (fr, len / 2 ); Complex tG = {cos (2 * Pi / len), sin (2 * Pi / len)}; Complex buf = {1 , 0 }; for (int k = 0 ; k < len / 2 ; k++) { sav[k] = fl[k] + buf * fr[k]; sav[k + len / 2 ] = fl[k] - buf * fr[k]; buf = buf * tG; } for (int k = 0 ; k < len; k++) f[k] = sav[k]; } void idft (Complex *f, int len) { if (len == 1 ) return ; Complex *fl = f, *fr = f + len / 2 ; for (int k = 0 ; k < len; k++) sav[k] = f[k]; for (int k = 0 ; k < len / 2 ; k++) { fl[k] = sav[k << 1 ]; fr[k] = sav[k << 1 | 1 ]; } idft (fl, len / 2 ); idft (fr, len / 2 ); Complex tG = {cos (2 * Pi / len), -sin (2 * Pi / len)}; Complex buf = {1 , 0 }; for (int k = 0 ; k < len / 2 ; k++) { sav[k] = fl[k] + buf * fr[k]; sav[k + len / 2 ] = fl[k] - buf * fr[k]; buf = buf * tG; } for (int k = 0 ; k < len; k++) f[k] = sav[k]; }
多项式乘法-高效版/位逆序版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> #include <bits/stdc++.h> using namespace std; const int maxn = 1000000 + 7 ; #define PI acos(-1) struct Complex { double x, y; Complex operator +(const Complex &b) const { return {x + b.x, y + b.y}; } Complex operator -(const Complex &b) const { return {x - b.x, y - b.y}; } Complex operator *(const Complex &b) const { return {x * b.x - y * b.y, x * b.y + y * b.x}; } }; int n,m; Complex a[maxn*3 ], b[maxn*3 ]; int pos[maxn*3 ]; void FFT (Complex*A, int len, int type) ; int main () { int x;pos[0 ] = 0 ; int maxLen = 1 , l = 0 ; scanf ("%d%d" , &n, &m); while (maxLen < n+m+1 ){ maxLen<<=1 ; l++; } for (int i = 0 ;i<=n;i++){ scanf ("%lf" ,&a[i].x); } for (int i = 0 ;i<=m;i++){ scanf ("%lf" ,&b[i].x); } for (int i = 0 ;i<maxLen;i++){ pos[i] = (pos[i>>1 ]>>1 )|((i&1 )<<(l-1 )); } FFT (a,maxLen,1 ); FFT (b,maxLen,1 ); for (int i = 0 ;i<maxLen;i++) a[i] = a[i]*b[i]; FFT (a,maxLen,-1 ); for (int i=0 ; i<n+m+1 ; i++){ if (i!=0 ) printf (" " ); printf ("%d" ,(int )(a[i].x+0.49 )); } printf ("\n" ); return 0 ; } void FFT (Complex*A, int len, int type) { for (int i=0 ; i<len; i++){ if (i<pos[i]) swap (A[i], A[pos[i]]); } for (int L=2 ; L<=len; L<<=1 ){ int HLen = L/2 ; Complex Wn = {cos (2.0 *PI/L), type*sin (2.0 *PI/L)}; for (int R=0 ; R<len; R+=L){ Complex w = {1 ,0 }; for (int k=0 ; k<HLen; k++, w=w*Wn){ Complex Buf = A[R+k]; A[R+k] = A[R+k] + w*A[R+k+HLen]; A[R+k+HLen] = Buf - w*A[R+k+HLen]; } } } if (type == -1 ) { for (int i = 0 ; i < len; i++) { A[i].x /= len; A[i].y /= len; } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <bits/stdc++.h> using namespace std; const int maxn = 1000000 + 7 ; #define PI acos(-1) struct Complex { int x, y; Complex operator +(const Complex &b) const { return {x + b.x, y + b.y}; } Complex operator -(const Complex &b) const { return {x - b.x, y - b.y}; } Complex operator *(const Complex &b) const { return {x * b.x - y * b.y, x * b.y + y * b.x}; } }; int n,m; Complex a[maxn*3 ], b[maxn*3 ]; int pos[maxn*3 ]; void FFT (Complex*A, int len, int type) ; int main () { int x;pos[0 ] = 0 ; int maxLen = 1 , l = 0 ; scanf ("%d" , &n); while (maxLen < n){ maxLen<<=1 ; l++; } for (int i = 0 ;i<n;i++){ scanf ("%d" ,&a[i].x); } for (int i = 0 ;i<n;i++){ pos[i] = (pos[i>>1 ]>>1 )|((i&1 )<<(l-1 )); } FFT (a,maxLen,1 ); for (int i=0 ; i<n; i++){ printf ("%d " , a[i].x); } printf ("\n" ); return 0 ; } void FFT (Complex*A, int len, int type) { for (int i=0 ; i<len; i++){ if (i<pos[i]) swap (A[i], A[pos[i]]); } }
高精度乘法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #include <iostream> #include <bits/stdc++.h> using namespace std; const int maxn = 1000000 + 7 ; #define PI acos(-1) struct Complex { double x, y; Complex operator +(const Complex &b) const { return {x + b.x, y + b.y}; } Complex operator -(const Complex &b) const { return {x - b.x, y - b.y}; } Complex operator *(const Complex &b) const { return {x * b.x - y * b.y, x * b.y + y * b.x}; } }; int n,m; Complex a[maxn*3 ], b[maxn*3 ]; long long ans[maxn*3 ]; int pos[maxn*3 ]; void FFT (Complex*A, int len, int type) ; int main () { int t; scanf ("%d" , &t); while (t--){ int x;pos[0 ] = 0 ; int maxLen = 1 , l = 0 ; char str1[maxn], str2[maxn]; scanf (" %s %s" , str1, str2); n = strlen (str1); m = strlen (str2); while (maxLen < n+m+1 ){ maxLen<<=1 ; l++; } for (int i=0 ; i<=maxLen; i++){ a[i].y = a[i].x = b[i].y = b[i].x = ans[i] = 0 ; } for (int i = 0 ; i < n; i++) { a[i].x = str1[n - i - 1 ] - '0' ; } for (int i = 0 ; i < m; i++) { b[i].x = str2[m - i - 1 ] - '0' ; } for (int i = 0 ;i<maxLen;i++){ pos[i] = (pos[i>>1 ]>>1 )|((i&1 )<<(l-1 )); } FFT (a,maxLen,1 ); FFT (b,maxLen,1 ); for (int i = 0 ;i<maxLen;i++) a[i] = a[i]*b[i]; FFT (a,maxLen,-1 ); for (int i = 0 ; i < maxLen; i++){ ans[i] += round (a[i].x); ans[i + 1 ] += ans[i] / 10 ; ans[i] %= 10 ; } int t1 = maxLen; while (ans[t1] == 0 && t1 > 0 ) t1--; while (t1 >= 0 ) printf ("%lld" , ans[t1--]); cout << endl; } return 0 ; } void FFT (Complex*A, int len, int type) { for (int i=0 ; i<len; i++){ if (i<pos[i]) swap (A[i], A[pos[i]]); } for (int L=2 ; L<=len; L<<=1 ){ int HLen = L/2 ; Complex Wn = {cos (2.0 *PI/L), type*sin (2.0 *PI/L)}; for (int R=0 ; R<len; R+=L){ Complex w = {1 ,0 }; for (int k=0 ; k<HLen; k++, w=w*Wn){ Complex Buf = A[R+k]; A[R+k] = A[R+k] + w*A[R+k+HLen]; A[R+k+HLen] = Buf - w*A[R+k+HLen]; } } } if (type == -1 ) { for (int i = 0 ; i < len; i++) { A[i].x /= len; A[i].y /= len; } } }
最大公约数:欧几里得算法
1 2 3 4 5 6 7 int euclid (int a, int b) { if (b == 0 ){ return a; }else { return euclid (b, a%b); } }
欧几里得算法的扩展形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <bits/stdc++.h> using namespace std;int d, x, y; void extendedEuclid (int a, int b) { if (b == 0 ){ d = a, x = 1 , y = 0 ; }else { extendedEuclid (b, a%b); int tempX = x; x = y, y = tempX-(int )floor (a/b)*y; } }int main () { int a, b; scanf ("%d%d" , &a, &b); extendedEuclid (a, b); printf ("%d = gcd(%d, %d) = %d*%d+%d*%d" , d, a, b, a, x, b, y); return 0 ; }
同余方程 $ax ≡ b (mod\ m)$
$ax ≡ b (mod\ m)$ 即 $ax - b = km$
通解形式 :线性同余方程 ax ≡ b (mod m)
的通解可以表示为 x = x0 + km/d
,其中 x0
是一个特解,k
是任意整数,d = gcd(a, m)
。
遍历所有解 :解的周期性为 d
,我们可以通过遍历 k
从 0
到 d-1
来找到所有可能的解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int f (long long a,long long b,long long m) { extendedEuclid (a,m); if (b%d) return -1 ; x=x*(b/d)%m; for (int i=1 ; i<=d; i++){ long long tempAns = (x+(i-1 )*m/d)%m; while (tempAns<0 ){ tempAns += m; } if (tempAns < ans){ ans = tempAns; } } printf ("%lld" , ans); }
字符串匹配
Rabin-Karp算法 双哈希
预处理时间 $\Theta(m)$,最坏运行时间 $\Theta((n-m+1)m)$,期望运行时间 $O(n)+O(m(v+\frac{n}{q}))$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <string> using namespace std; void Rabin_Karp_search (const string &T, const string &P, int d, int q1, int q2) ; int main () { string T = "Rabin–Karp string search algorithm: Rabin-Karp" ; string P = "Rabin" ; int q1 = 101 ; int q2 = 103 ; int d = 52 ; Rabin_Karp_search (T, P, d, q1, q2); return 0 ; } void Rabin_Karp_search (const string &T, const string &P, int d, int q1, int q2) { int m = P.length (); int n = T.length (); int i, j; int p1 = 0 , p2 = 0 ; int t1 = 0 , t2 = 0 ; int h1 = 1 , h2 = 1 ; for (i = 0 ; i < m-1 ; i++){ h1 = (h1*d)%q1; h2 = (h2*d)%q2; } for (i = 0 ; i < m; i++) { p1 = (d*p1 + P[i])%q1; p2 = (d*p2 + P[i])%q2; t1 = (d*t1 + T[i])%q1; t2 = (d*t2 + T[i])%q2; } for (i = 0 ; i <= n - m; i++) { if ( p1 == t1 && p2 == t2 ) { for (j = 0 ; j < m; j++) if (T[i+j] != P[j]) break ; if (j == m) cout<<"Pattern found at index :" <<i<<"\n" ; } if ( i < n-m ){ t1 = (d*(t1 - T[i]*h1) + T[i+m])%q1; t2 = (d*(t2 - T[i]*h2) + T[i+m])%q2; if (t1 < 0 ) t1 = (t1 + q1); if (t2 < 0 ) t2 = (t2 + q2); } } }
字符匹配有限自动机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <bits/stdc++.h> using namespace std; #include <string> #define total_chars 256 #define trans_func(i, j) (trans_func[(i) * (m+1) + (j)]) void transition (int * trans_func, string &pattern) ; void FSA (string &pattern, string &text) ; int main () { string pattern = "abc" ; string text = "abcabcdefghabc" ; FSA (pattern, text); } void FSA (string &pattern, string &text) { int m = pattern.length (); int n = text.length (); int * trans_func = new int [total_chars * m]; transition (trans_func, pattern); int q = 0 ; for (int i = 0 ; i < n; i++) { q = trans_func (text[i], q); if (q == m) { printf ("Pattern found at index :%d " , i-m+1 ); q = 0 ; } } long long ans = 0 ; for (int j=0 ; j<total_chars; j++){ for (int i = (m+1 )*j+0 ; i < (m+1 )*j+m+1 ; i++) { ans += trans_func[i]; } } } void transition (int * trans_func, string &pattern) { int m = pattern.length (); for (int i = 0 ; i < total_chars; i++) { if (i == pattern[0 ]) { trans_func (i, 0 ) = 1 ; } else { trans_func (i, 0 ) = 0 ; } } int X = 0 ; for (int j = 1 ; j < m; j++) { for (int i = 0 ; i < total_chars; i++) { if (pattern[j] == i) { trans_func (i, j) = j + 1 ; } else { trans_func (i, j) = trans_func (i, X); } } X = trans_func (pattern[j], X); } for (int i = 0 ; i < total_chars; i++) { if (pattern[X] == i) { trans_func (i, m) = X + 1 ; } else { trans_func (i, m) = trans_func (i, X); } } }
KMP算法
时间复杂度 $O(m+n)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> #include <vector> #include <string> using namespace std; vector<int > prefix (string str) ; int main () { string text; string key; cin >> text; cin >> key; int kl = key.length (); vector<int > kmp = prefix (key); int k = 0 ; for (int i = 0 ; i < text.length (); i++){ while (k && key[k] != text[i]) k = kmp[k - 1 ]; if (text[i] == key[k]) k++; if (k == kl) cout << i - k + 2 << "\n" ; } for (auto x: kmp) cout << x << " " ; return 0 ; } vector<int > prefix (string str) { int l = (int ) str.length (); vector<int > pre (l) ; for (int i = 1 ; i < l; i++){ int j = pre[i - 1 ]; while (j && str[j] != str[i]) j = pre[j - 1 ]; if (str[j] == str[i]) j++; pre[i] = j; } return pre; }
确定连续线段是向左转还是向右转
已知 $\overrightarrow{p_{0}p_{1}}, \overrightarrow{p_{1}p_{2}}$ 。计算 $(p_{2}-p_{0})\times(p_{1}-p_{0}) = (x_{2}-x_{0})(y_{1}-y_{0})-(y_{2}-y_{0})(x_{1}-x_{0})$
结果 $<0$ ,则在 $p_{1}$ 左转
结果 $>0$ ,则在 $p_{1}$ 右转
结果 $=0$ ,则在 $p_{0}, p_{1}, p_{2}$ 三者共线
1 2 3 4 5 6 7 8 9 10 struct dot { int x, y; }; int direction (dot pi, dot pj, dot pk) { return ((pk.x-pi.x)*(pj.y-pi.y)-(pk.y-pi.y)*(pj.x-pi.x)); }
判定两条线是否相交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct dot { int x, y; }; int direction (dot pi, dot pj, dot pk) { return ((pk.x-pi.x)*(pj.y-pi.y)-(pk.y-pi.y)*(pj.x-pi.x)); } bool onSegment (dot pi, dot pj, dot pk) { if (min (pi.x, pj.x) <= pk.x && max (pi.x, pj.x) >= pk.x && min (pi.y, pj.y) <= pk.y && max (pi.y, pj.y) >= pk.y){ return true ; } return false ; } bool segmentsIntersect (dot p1, dot p2, dot p3, dot p4) { int d1 = direction (p3, p4, p1); int d2 = direction (p3, p4, p2); int d3 = direction (p1, p2, p3); int d4 = direction (p1, p2, p4); if (((d1>0 && d2<0 )||(d1<0 && d2>0 )) && ((d3>0 && d4<0 )||(d3<0 && d4>0 ))){ return true ; }else if (d1 == 0 && onSegment (p3, p4, p1)){ return true ; }else if (d2 == 0 && onSegment (p3, p4, p2)){ return true ; }else if (d3 == 0 && onSegment (p1, p2, p3)){ return true ; }else if (d4 == 0 && onSegment (p1, p2, p4)){ return true ; }else { return false ; } }
确定任意一对线段是否相交
$O(n^2)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <iostream> #include <algorithm> #include <vector> using namespace std; struct Point { int x, y; Point operator +(const Point &b) const { return {x + b.x, y + b.y}; } Point operator -(const Point &b) const { return {x - b.x, y - b.y}; } Point operator *(const int &b) const { return {x * b, y * b}; } int operator ^(const Point &b) const { return x * b.y - y * b.x; } }; struct Line { Point p; Point q; }; vector<Line> lines; int sgn (int x) ; bool intersect (Line l1, Line l2) ; bool onSegment (Point point, Line line) ; int main () { int n, cnt = 0 ; cin >> n; int x1, y1, x2, y2; for (int i = 0 ; i < n; i++) { cin >> x1 >> y1 >> x2 >> y2; lines.push_back ({{x1, y1}, {x2, y2}}); } for (int i = 0 ; i < n; i++) for (int j = 0 ; j < i; j++) if (intersect (lines[i], lines[j])) cnt++; cout << cnt; return 0 ; } int sgn (int x) { if (x < 0 ) return -1 ; if (x > 0 ) return 1 ; return 0 ; } bool intersect (Line l1, Line l2) { int d1 = sgn ((l1.q - l1.p) ^ (l2.p - l1.p)); int d2 = sgn ((l1.q - l1.p) ^ (l2.q - l1.p)); int d3 = sgn ((l2.q - l2.p) ^ (l1.p - l2.p)); int d4 = sgn ((l2.q - l2.p) ^ (l1.q - l2.p)); if (d1 * d2 < 0 && d3 * d4 < 0 ) return true ; if (d1 == 0 && onSegment (l2.p, l1)) return true ; if (d2 == 0 && onSegment (l2.q, l1)) return true ; if (d3 == 0 && onSegment (l1.p, l2)) return true ; if (d4 == 0 && onSegment (l1.q, l2)) return true ; return false ; } bool onSegment (Point point, Line line) { if (point.x >= min (line.p.x, line.q.x) && point.x <= max (line.p.x, line.q.x) && point.y >= min (line.p.y, line.q.y) && point.y <= max (line.p.y, line.q.y)) return true ; return false ; }
寻找凸包(Graham扫描法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #include <iostream> #include <algorithm> #include <cmath> #include <cstdio> using namespace std; const int MAX = 200005 ; const double eps = 1e-7 ; struct Point { double x, y; Point operator +(const Point &b) const { return {x + b.x, y + b.y}; } Point operator -(const Point &b) const { return {x - b.x, y - b.y}; } double operator ^(const Point &b) const { return x * b.y - y * b.x; } bool operator <(const Point &b) const { if (x != b.x) return x < b.x; return y < b.y; } }; Point p[MAX]; Point s[MAX]; int top; void selMin (int n) ; int cmp (Point a, Point b) ; bool equal (double a, double b) ; double dis (Point a, Point b) ; void graham (int n) ; double s_sqr (Point a, Point b, Point c) ; double diameter () ; int main () { int n; cin >> n; for (int i = 0 ; i < n; i++) cin >> p[i].x >> p[i].y; selMin (n); sort (p + 1 , p + n, cmp); graham (n); printf ("%.6f" , sqrt (diameter ())) ; return 0 ; } void selMin (int n) { Point Min = p[0 ]; int IDMin = 0 ; for (int i = 0 ; i < n; i++) if (p[i] < Min) { Min = p[i]; IDMin = i; } swap (p[0 ], p[IDMin]); } int cmp (Point a, Point b) { double x = (a - p[0 ]) ^ (b - p[0 ]); if (x > 0 ) return 1 ; if (equal (x, 0 ) && (dis (a, p[0 ]) < dis (b, p[0 ]))) return 1 ; return 0 ; } double dis (Point a, Point b) { double x = a.x - b.x; double y = a.y - b.y; return x * x + y * y; } void graham (int n) { top = 1 ; s[0 ] = p[0 ]; s[1 ] = p[1 ]; for (int i = 2 ; i < n; i++) { while (top > 1 && ((p[i] - s[top]) ^ (s[top - 1 ] - s[top])) <= 0 ) top--; s[++top] = p[i]; } } double s_sqr (Point a, Point b, Point c) { return fabs ((a - b) ^ (c - b)); } double diameter () { double diam = 0 ; int j = 2 ; s[++top] = s[0 ]; if (top < 3 ) return dis (s[0 ], s[1 ]); for (int i = 0 ; i < top - 1 ; i++) { while (s_sqr (s[i], s[i + 1 ], s[j]) < s_sqr (s[i], s[i + 1 ], s[(j + 1 ) % top])) j = (j + 1 ) % top; diam = max (diam, max (dis (s[i], s[j]), dis (s[i + 1 ], s[j]))); } return diam; } bool equal (double a, double b) { return fabs (a - b) < eps; }
点、向量相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 struct point { double x,y; int quad () { if (x>0 &&y>=0 ) return 1 ;if (x<=0 &&y>0 ) return 2 ; if (x<0 &&y<=0 ) return 3 ;if (x>=0 &&y<0 ) return 4 ; } };bool sortXupYup (point u,point v) { if (u.x!=v.x) return u.x<v.x; else return u.y<v.y; } bool sortYupXup (point u,point v) { if (u.y!=v.y) return u.y<v.y; else return u.x<v.x; } bool sortPointAngle (point a,point b) { if (a.quad ()!=b.quad ()) return a.quad ()<b.quad (); return (a^b)>0 ; }double norm (point u) { return sqrt (u.x*u.x+u.y*u.y); }double disPointPoint (point u,point v) { return sqrt ((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y)); } point SpinPoint (point u,double zhita) { double r=sqrt (u.x*u.x+u.y*u.y); double sinphi=u.y/r,cosinphi=u.x/r; double sinr=sinphi*cos (zhita)+cosinphi*sin (zhita); double cosr=cosinphi*cos (zhita)-sinphi*sin (zhita); point v; v.x=r*cosr;v.y=r*sinr; return v; } double mindisset1 (point* a,int size,int &u,int &v) { double minn=1e18 ; sort (a+1 ,a+1 +size,sortXupYup); Nearestpoint (a,1 ,size,u,v,minn); return minn; } void Nearestpoint (point *a,int l,int r,int &u,int &v,double &d) { if (l==r) return ; if (l+1 ==r) { if (disPointPoint (a[l],a[r])<d) { d = disPointPoint (a[l], a[r]); u = l, v = r; } return ; } int mid=l+r>>1 ,m=0 ; Nearestpoint (a,l,mid,u,v,d),Nearestpoint (a,mid+1 ,r,u,v,d); point b[r-l+10 ]; for (int i=l;i<=r;i++){ if (abs (a[i].x-a[mid].x)<d) b[++m]=a[i]; } sort (b+1 ,b+1 +m,sortYupXup); for (int i=1 ;i<=m;i++){ for (int j=i+1 ;j<=m&&abs (b[i].y-b[j].y)<d;j++){ if (d>disPointPoint (b[i],b[j])) { d = disPointPoint (b[i], b[j]); u = i, v = j; } } } return ; }
maybe凸包相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 double cosinesLaw (double a,double b,double c) { return (a*a+b*b-c*c)/(2 *a*b); } double triarea (point u,point v,point w) { return abs ((v-u)^(w-u))/2.0 ; } double triarea (double a,double b,double c) { double p=(a+b+c)/2 ; return sqrt (p*(p-a)*(p-b)*(p-c)); } double polygonArea (point *u,int size) { double area=0 ; point begin=u[0 ]; for (int i=2 ;i<size;i++) area+=((u[i-1 ]-begin)^(u[i]-begin))/2 ; return area; } bool Isinside (point u,point *v,int size) { if (((v[size]-v[1 ])^(u-v[1 ]))>0 ||((v[2 ]-v[1 ])^(u-v[1 ]))<0 ) return false ; int l=2 ,r=size; while (l<=r){ int mid=l+r>>1 ; if (((v[mid]-v[1 ])^(u-v[1 ]))<0 ) l=mid+1 ; else r=mid-1 ; } if (((v[r]-v[l])^(u-v[l]))>=0 ) return true ; return false ; }
直线相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 struct line { double a,b,c; point u,v; line (){} line (point p,point q){ a=p.y-q.y;b=q.x-p.x;c=p.x*q.y-q.x*p.y; if ((p^q)<0 ) swap (p,q); u=p;v=q-p; } }; bool sortLineAngle (line a,line b) { if (a.v.quad ()!=b.v.quad ()) return a.v.quad ()<b.v.quad (); else return (a.v^b.v)==0 ?(a.v^(a.u-b.u))>0 :(a.v^b.v)>0 ; } double disPointLine (point u,line l) { double length; length = abs (l.a*u.x+l.b*u.y+l.c)/(sqrt (l.a*l.a+l.b*l.b)); return length; } point proPointLine (point u,line l) { point v; double t=(-u.x*l.a-u.y*l.b-l.c)/(l.a*l.a+l.b*l.b); v.x=u.x+l.a*t; v.y=u.y+l.b*t; return v; } double disPointSeg (point u, point v, point w) { long long d1 = (u.x - v.x) * (w.x - v.x) + (u.y - v.y) * (w.y - v.y); if (d1 <= 0.0 ) return sqrt ((u.x - v.x) * (u.x - v.x) + (u.y - v.y) * (u.y - v.y)); long long d2 = (v.x - w.x) * (v.x - w.x) + (v.y - w.y) * (v.y - w.y); if (d1 >= d2) return sqrt ((u.x - w.x) * (u.x - w.x) + (u.y - w.y) * (u.y - w.y)); double r = 1.0 * d1 / d2; double px = v.x + (w.x - v.x) * r; double py = v.y + (w.y - v.y) * r; return sqrt ((u.x - px) * (u.x - px) + (u.y - py) * (u.y - py)); } point itsLineLine (line l1, line l2) { point p; double k = l1.a * l2.b - l1.b * l2.a; p.x = -(l1.c * l2.b - l1.b * l2.c) / k; p.y = -(l1.a * l2.c - l1.c * l2.a) / k; return p; } const line bd[4 ] = { line (point{-INF,-INF},point{INF,-INF}),line (point{INF,-INF},point{INF,INF}), line (point{INF,INF},point{-INF,INF}),line (point{-INF,INF},point{-INF,-INF}), }; vector<point> HalfPlaneInter (vector<line> k) { for (int i = 0 ; i < 4 ; i++) k.push_back (bd[i]); sort (k.begin (), k.end (), sortLineAngle); deque<line> q; deque<point> c; vector<point> ans; int m = 0 ; for (int i = 1 ; i < k.size (); i++) if ((k[m].v ^ k[i].v) != 0 ) k[++m] = k[i]; q.push_back (k[0 ]); for (int i = 1 ; i <= m; i++) { while (c.size () && ((c.back () - k[i].u) ^ k[i].v) >= 0 ) { q.pop_back (); c.pop_back (); } while (c.size () && ((c.front () - k[i].u) ^ k[i].v) >= 0 ) { q.pop_front (); c.pop_front (); } c.push_back (itsLineLine (k[i], q.back ())); q.push_back (k[i]); } while (c.size () && ((c.back () - q.front ().u) ^ q.front ().v) >= 0 ) { q.pop_back (); c.pop_back (); } while (c.size ()) { ans.push_back (c.front ()); c.pop_front (); } if (q.size () > 1 ) ans.push_back (itsLineLine (q.front (), q.back ())); return ans; }
圆相关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 struct circle { point cc; double radius; }; circle concyclic (point u, point v, point w) { circle c; point o; double k = 2 * (v.x - u.x) * (w.y - v.y) - 2 * (v.y - u.y) * (w.x - v.x); o.x = (w.y - v.y) * (v.x * v.x + v.y * v.y - u.x * u.x - u.y * u.y) - (v.y - u.y) * (w.x * w.x + w.y * w.y - v.x * v.x - v.y * v.y); o.y = (v.x - u.x) * (w.x * w.x + w.y * w.y - v.x * v.x - v.y * v.y) - (w.x - v.x) * (v.x * v.x + v.y * v.y - u.x * u.x - u.y * u.y); o.x /= k; o.y /= k; c.cc = o; c.radius = disPointPoint (o, u); return c; } vector<point> itsStrCir (line l, circle c) { double k = l.u * l.v; double a = norm (l.u), b = norm (l.v); double r = c.radius; double d = k * k - b * b * (a * a - r * r); vector<point> ans; if (d == 0 ) ans.push_back (l.u + l.v * (-k / (b * b))); else { ans.push_back (l.u + l.v * ((-k + d) / (b * b))); ans.push_back (l.u + l.v * ((-k - d) / (b * b))); } return ans; } vector<point> itsCirCir (circle c1, circle c2) { vector<point> ans; point o1 = c1.cc, o2 = c2.cc; point a = o2 - o1; point b; b.x = a.y; b.y = -a.x; double r1 = c1.radius, r2 = c2.radius; double d = disPointPoint (o1, o2); double S = triarea (r1, r2, d); double h = 2 * S / d; double t = sqrt (r1 * r1 - h * h); if (r1 * r1 + d * d < r2 * r2) t = -t; if (h == 0 ) { ans.push_back (o1 + a * t / norm (a)); } else { ans.push_back (o1 + a * t / norm (a) + b * h / norm (b)); ans.push_back (o1 + a * t / norm (a) - b * h / norm (b)); } return ans; } vector<line> tlPointCircle (point u, circle c) { vector<line> ans; circle o; o.cc = (c.cc + u) / 2 ; o.radius = disPointPoint (c.cc, u) / 2 ; vector<point> p = itsCirCir (o, c); if (p.size () == 1 ) { point v; v.x = (u - c.cc).y; v.y = -(u - c.cc).x; ans.push_back (line (u, u + v)); } if (p.size () == 2 ) { ans.push_back (line (p[0 ], u)); ans.push_back (line (p[1 ], u)); } return ans; } vector<line> comTangent (circle c1, circle c2) { vector<line> ans, q; int r1 = c1.radius, r2 = c2.radius; int d = disPointPoint (c1.cc, c2.cc); point u, v, a = c2.cc - c1.cc, t; if (r1 == r2) { u = c1.cc - c2.cc; v.x = u.y; v.y = -u.x; ans.push_back (line (c1.cc + v * r1 / norm (v), c1.cc + v * r1 / norm (v) + u)); ans.push_back (line (c1.cc - v * r1 / norm (v), c1.cc - v * r1 / norm (v) + u)); } else { if (triarea (r1, r2, d) == 0 ) { t = c1.cc + a * r1 / r2; q = tlPointCircle (t, c1); while (q.size ()) { ans.push_back (q.back ()); q.pop_back (); } } t = c1.cc + a * r1 / (r1 - r2); q = tlPointCircle (t, c1); while (q.size ()) { ans.push_back (q.back ()); q.pop_back (); } } return ans; } circle Smallestcir (point *u, int size) { random_shuffle (u + 1 , u + 1 + size); point o = u[1 ]; double r = 0 ; for (int i = 2 ; i <= size; i++) { if (disPointPoint (o, u[i]) <= r) continue ; o = (u[i] + u[1 ]) / 2 ; r = disPointPoint (u[i], u[1 ]) / 2 ; for (int j = 2 ; j < i; j++) { if (disPointPoint (u[j], o) <= r) continue ; o = (u[i] + u[j]) / 2 ; r = disPointPoint (u[i], u[j]) / 2 ; for (int k = 1 ; k < j; k++) { if (disPointPoint (u[k], o) <= r) continue ; circle c = concyclic (u[i], u[j], u[k]); o = c.cc; r = c.radius; } } } circle c; c.cc = o; c.radius = r; return c; }
快速幂取余
1 2 3 4 5 6 7 8 9 10 11 12 ll fast_pow_mod (ll a, ll b, ll m) { a %= m; ll res = 1 ; while (b > 0 ) { if (b & 1 ) res = res * a % m; a = a * a % m; b >>= 1 ; } return res; }
快速打质数表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 vector<int > generate_prime_list (int n) { if (n <= 2 ) return vector<int >{2 }; if (n <= 3 ) return vector<int >{2 , 3 }; if (n <= 5 ) return vector<int >{2 , 3 , 5 }; vector<int > prime_list = {2 , 3 , 5 }; int i = 1 ; int x; while (true ) { x = 6 * i + 1 ; if (x > n) break ; if (is_prime (x, prime_list)) prime_list.push_back (x); x = 6 * i + 5 ; if (x > n) break ; if (is_prime (x, prime_list)) prime_list.push_back (x); i++; } return prime_list; } bool is_prime (int x, const vector<int > &prime_list) { for (auto u: prime_list){ if (x % u == 0 ) return false ; if (u * u > x) return true ; } return true ; }
单调栈
寻找左 侧第一个比当前元素大 的元素:从左到右遍历元素,构造单调递增栈 (从栈顶到栈底递增)
一个元素左侧第一个比它大的元素就是将其「插入单调递增栈 」时的栈顶元素。
如果插入时的栈为空,则说明左侧不存在比当前元素大的元素。
寻找左 侧第一个比当前元素小 的元素:从左到右遍历元素,构造单调递减栈 (从栈顶到栈底递减)
一个元素左侧第一个比它小的元素就是将其「插入 单调递减栈」时的栈顶元素。
如果插入时的栈为空,则说明左侧不存在比当前元素小的元素。
寻找右 侧第一个比当前元素大 的元素:从左到右遍历元素,构造单调递增栈 (从栈顶到栈底递增)
一个元素右侧第一个比它大的元素就是将其「弹出 单调递增栈」时即将插入的元素。
如果该元素没有被弹出栈,则说明右侧不存在比当前元素大的元素。
寻找右 侧第一个比当前元素小 的元素:从左到右遍历元素,构造单调递减栈 (从栈顶到栈底递减)
一个元素右侧第一个比它小的元素就是将其「弹出 单调递减栈」时即将插入的元素。
如果该元素没有被弹出栈,则说明右侧不存在比当前元素小的元素。
查找 「比当前元素大的元素」 就用 单调递增栈 ,查找 「比当前元素小的元素」 就用 单调递减栈 。
从 「左侧」 查找就看 「插入栈」 时的栈顶元素,从 「右侧」 查找就看 「弹出栈」 时即将插入的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <cstdio> #define MAX (300000+10) int monoIncreaseStack (int height, int id) ; void push (long long height, long long id) ; long long pop () ; struct student { long long id; long long height; long long leftBigId, rightBigId; }; struct student stacks[MAX]; struct student students[MAX]; int Top = -1 ; int main () { int t; scanf ("%d" , &t); while (t--){ int n; Top = -1 ; scanf ("%d" , &n); for (int i = 0 ; i < n; ++i) { long long height; scanf ("%lld" , &height); students[i].height = height; students[i].id = i; monoIncreaseStack (height, i); } while (Top != -1 ){ students[stacks[Top].id].rightBigId = n; pop (); } } return 0 ; } int monoIncreaseStack (int height, int id) { while (Top!=-1 && height>=stacks[Top].height){ long long popId = pop (); students[popId].rightBigId = id; } if (Top == -1 ){ students[id].leftBigId = -1 ; }else { students[id].leftBigId = stacks[Top].id; } push (height, id); } void push (long long height, long long id) { stacks[++Top].height = height; stacks[Top].id = id; } long long pop () { return stacks[Top--].id; }
秦九韶算法/Horner 规则
$A(x)=a_{n}x_{n}+a_{n−1}x_{n−1}+…+a_{1}x+a_{0}$ 在 $x_{0}$ 处的值相当于 $a_0+x_0(a_{1}+…+x_{0}(a_{n−1}+x_{0}a_{n}))$
大数相乘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <stdio.h> #include <string.h> #define MAX 1000005 char s1[MAX], s2[MAX]; int a1[MAX], a2[MAX], ans[MAX]; int main () { int n; scanf("%d" , &n); for (int z=0 ; z<n; z++){ int i, j, len1, len2; scanf("%s%s" , s1, s2); len1 = strlen(s1); len2 = strlen(s2); for (i = 0 ; i < len1; i++) { a1[i] = s1[len1 - 1 - i] - '0' ; } for (i = 0 ; i < len2; i++) { a2[i] = s2[len2 - 1 - i] - '0' ; } for (i = 0 ; i < len1; i++) { for (j = 0 ; j < len2; j++) { ans[i + j] += a1[i] * a2[j]; } } for (i = 0 , j = 0 ; i < len1 + len2; i++) { ans[i] += j; j = ans[i] / 10 ; ans[i] %= 10 ; } int flag = 0 ; for (; ans[i] == 0 ; i--){ if (i<0 ){ printf("0" ); puts("" ); flag = 1 ; break ; } } if (flag == 0 ){ for (; i >= 0 ; i--) { printf("%d" , ans[i]); ans[i] = 0 ; } puts("" ); } } return 0 ; }
随机数
1 2 3 4 inline int Rand () { return rand ()%100 ; }
模逆元
模逆元:$a×x≡1 (mod m)$
如果p是一个质数,a是任意整数,且a不是p的倍数,那么a的模逆元可以表示为:$a^{-1} \equiv a^{p-2} \ (\text{mod} \ p)$
即
1 2 3 4 5 6 7 8 9 10 long long qmi (long long mom, long long b, long long mod) { long long res = 1 ; while (b){ if (b & 1 ) res = res * mom % mod; b >>= 1 ; mom = mom * mom % mod; } return res; } x = qmi (a, MOD-2 , MOD);
MORE……