该篇为《算法导论》学习笔记,包含部分章节的理论阐释、典型问题、和代码实现。
算法
时间复杂度
1.渐近符号:
θ--渐近紧确界; f(n)=θ(g(n)):g(n)是f(n)的渐进紧确界.
定义:存在c1,c2,n0,对任意n>=n0,有:0<=c1g(n)<=f(n)<=c2 g(n).
f(n)=θ(g(n)),当且仅当:f(n)=O(g(n))且f(n)=欧姆 .
O--渐近上界;[欧姆]--渐近下界 o--非紧确渐近上界;ω--非紧确渐近下界
f(n)=O(g(n))中,0<=f(n)<cg(n)对某个常量c>0成立;
f(n)=o(g(n))中,0<=f(n)<c g(n)对所有常量c>0成立.
2.主定理求时间复杂度:
T(n)=aT(n/b)+f(n). 比较n和f(n):选择多项式意义上更大的。
1.f(n)=O(n(logb(a)-ε)),则:T(n)=θ(n (logb(a)).
2.f(n)=0(n(logb(a))),则:T(n)=θ(n (logb(a) lgn). 3.f(n)=欧姆 ,则:T(n)=θ(f(n)).
注意:T(n)=2T(n/2)+nlgn.
nlgn非多项式意义大于n,落入2,3间隙,不适用主定理.
快速幂/质数
1.快速幂:求a^b.
1 2 3 4 5 6 7 8 long long fpm (long long a,long long b) { long long ans=1 ; for (;b!=0 ;b>>=1 ,a=a*a%M) { if (b&1 ) ans=ans*a%M; } return ans; }
求矩阵n次幂:An--θ(n 3)->θ(n^2*logn). 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> #define maxn 205 typedef long long ll;const int p = 1e9 + 7 ;using namespace std ; int n;ll k; struct Martix { ll a[maxn][maxn]; }; inline Martix multiply (Martix x, Martix y) { Martix z; for (int i = 1 ;i <= n;i++){ for (int j = 1 ;j <= n;j++){ z.a[i][j] = 0 ; for (int k = 1 ;k <= n;k++){ z.a[i][j] += x.a[i][k] * y.a[k][j]; z.a[i][j] %= p; } } } return z; } inline Martix fpow (Martix x, ll k) { Martix y; for (int i = 1 ;i <= n;i++){ for (int j = 1 ;j <= n;j++){ if (i == j) y.a[i][j] = 1 ; else y.a[i][j] = 0 ; } } while (k){ if (k & 1 ) y = multiply(y, x); x = multiply(x, x); k >>= 1 ; } return y; } int main () { int t; scanf ("%d" , &t); while (t--){ scanf ("%d%" , &n);k = n; Martix x; for (int i = 1 ;i <= n;i++){ for (int j = 1 ;j <= n;j++){ scanf ("%lld" , &x.a[i][j]); } } x = fpow(x, k); for (int i = 1 ;i <= n;i++){ for (int j = 1 ;j <= n;j++){ printf ("%lld " , x.a[i][j]); } printf ("\n" ); } } }
2.分解质因数
质数个数:O(n/logn). (1)朴素算法:遍历i. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int a[N],p[N],k=0 ; void decompose (int n) { for (int i=2 ;i*i<=n;i++){ if (n%i==0 ) { p[k]=i;a[k]=0 ; while (n%i==0 ) {a[k]++;n/=i;} k++; } } if (n!=1 ){ p[k]=n;a[k]=1 ;k++; } }
(2)优化:预先运用埃氏筛/欧氏筛打表出2~n的质数,改为遍历prime[i].
埃氏筛:O(n*loglogn) 欧式筛:O(n)
3.逆元:若x*y=1(mod
p),则x,y互为模p意义下的逆元.
由费马小定理:x^(-1)=x^(p-2)(mod p),p为质数.
求阶乘的逆元:
递推实现(i!)^(-1)=((i+1)!)^(-1)*(i+1)(mod p).
令:inv[i]表示i!的逆元,则:inv[i-1]=inv[i]*i(mod p).
O(logn)求n!的逆元,O(n)递推得所有数的逆元.
4.秦九韶算法:快速计算sigma(ai*x^i)(i=0~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 long long a[30005 ],b[30005 ];#define E 10007 int main () { int n;scanf ("%d" ,&n); for (int i=0 ;i<=n;i++) scanf ("%lld" ,&a[i]); int m;scanf ("%d" ,&m); for (int i=0 ;i<=m;i++) scanf ("%lld" ,&b[i]); int q;long long x,y;scanf ("%d" ,&q); for (int i=0 ;i<q;i++) { scanf ("%lld%lld" ,&x,&y); x%=E;y%=E; long long sum1=0 ,mi=1 ; for (int i=0 ;i<=n;i++) { sum1=(sum1%E+(a[i]%E)*(mi%E))%E; mi=(mi*x)%E; } mi=1 ; long long sum2=0 ; for (int i=0 ;i<=m;i++) { sum2=(sum2%E+(b[i]%E)*(mi%E))%E; mi=(mi*y)%E; } long long res=((sum1%E)*(sum2%E))%E; printf ("%lld\n" ,res); } return 0 ; }
5.辗转相除法:
1 2 3 4 5 int gcd (int a,int b) { if (a==0 ||b==0 ) return a+b; else if (a%b==0 ) return b; else return gcd(b,a%b); }
递推/模拟
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 long long data[1005 ]={0 };#define MAX 998244353 void calculate (int num,int *now) { int k=*now; while (num>=k) { long long sum=0 ; for (int i=0 ;i<k;i++) { sum=(((data[i]%MAX)*(data[k-1 -i]%MAX))%MAX+sum)%MAX; } data[k]=sum%MAX; k++; } *now=k-1 ; } int main () { int n;scanf ("%d" ,&n);int now=1 ; data[0 ]=data[1 ]=1 ; for (int i=0 ;i<n;i++) { int num;scanf ("%d" ,&num); if (num<=now) printf ("%lld\n" ,data[num]); else { calculate(num,&now); printf ("%lld\n" ,data[num]); } } 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 40 41 42 43 44 45 46 int a[100005 ],b[100005 ],A[100005 ],B[100005 ],c[200005 ],C[200005 ];int main () { int t,n,m;scanf ("%d" ,&t); while (t--) { scanf ("%d" ,&n); for (int i=0 ;i<n;i++) scanf ("%d" ,&a[i]); for (int i=0 ;i<n;i++) scanf ("%d" ,&A[i]); scanf ("%d" ,&m); for (int i=0 ;i<m;i++) scanf ("%d" ,&b[i]); for (int i=0 ;i<m;i++) scanf ("%d" ,&B[i]); int i=0 ,j=0 ,k=0 ; while (i<n&&j<m) { if (A[i]==B[j]) { C[k]=A[i];c[k++]=a[i++]+b[j++]; } else if (A[i]<B[j]) { C[k]=A[i];c[k++]=a[i++]; } else { C[k]=B[j];c[k++]=b[j++]; } } while (i<n) {C[k]=A[i];c[k++]=a[i++];} while (j<m) {C[k]=B[j];c[k++]=b[j++];} printf ("%d\n" ,k); for (int t=0 ;t<k;t++) printf ("%d " ,c[t]); printf ("\n" ); for (int t=0 ;t<k;t++) printf ("%d " ,C[t]); printf ("\n" ); memset (a,0 ,sizeof (int )*i);memset (A,0 ,sizeof (int )*i); memset (b,0 ,sizeof (int )*j);memset (B,0 ,sizeof (int )*j); memset (c,0 ,sizeof (int )*k);memset (C,0 ,sizeof (int )*k); } return 0 ; }
3.大数乘法:θ(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 void multiply (char * a,char * b) { int len1=strlen (a); int len2=strlen (b); int *res=(int *)malloc (sizeof (int )*(len1+len2+1 )); for (int i=0 ;i<len1+len2;i++) res[i]=0 ; for (int i=0 ;i<len1;i++) for (int j=0 ;j<len2;j++) res[i+j+1 ]+=(a[i]-'0' )*(b[j]-'0' ); for (int i=len1+len2-1 ;i>0 ;i--) if (res[i]>=10 ) { res[i-1 ]+=res[i]/10 ; res[i]%=10 ; } int i=0 ; while (res[i]==0 &&i<len1+len2) i++; if (i==len1+len2) {printf ("0\n" );return ;} for (int j=i;j<len1+len2;j++) printf ("%c" ,res[j]+'0' ); printf ("\n" ); } char A[2005 ],B[2005 ];int main () { int T;scanf ("%d" ,&T);getchar(); while (T--) { memset (A,'\0' ,sizeof (char )*2005 );memset (B,'\0' ,sizeof (char )*2005 ); fgets(A,2004 ,stdin );fgets(B,2004 ,stdin ); int lenA=strlen (A);int lenB=strlen (B); A[lenA-1 ]='\0' ;B[lenB-1 ]='\0' ; multiply(A,B); } }
4.高精度除法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 double divide (long numerator,long denominator) { double offset=1.0 ; double q=0.0 ; long a=numerator; long b=denominator; while (b>1 ){ if (a>=b){ a=a-b; q=q+offset; }else { offset=offset*0.1 ; b=denominator*offset; } } return q; }
分治
1.最大子数组问题:寻找A的和最大的非空连续子数组.
朴素: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 #define INFINITY -2147483647 int findMaxCrossingSubarray (int *changes,int low,int mid,int high) { int left_sum=INFINITY,right_sum=INFINITY,sum=0 ; for (int i=mid;i>=low;i--) { sum+=changes[i]; if (sum>left_sum) left_sum=sum; } sum=0 ; for (int j=mid+1 ;j<=high;j++) { sum+=changes[j]; if (sum>right_sum) right_sum=sum; } return left_sum+right_sum; } int findMaxSubarray (int *changes,int low,int high) { if (low==high) return changes[low]; int mid=(low+high)/2 ; int leftSum=findMaxSubarray(changes,low,mid),rightSum=findMaxSubarray(changes,mid+1 ,high),crossSum=findMaxCrossingSubarray(changes,low,mid,high); if (leftSum>=rightSum&&leftSum>=crossSum) return leftSum; else if (rightSum>=leftSum&&rightSum>=crossSum) return rightSum; else return crossSum; } int maxProfit (int *prices,int pricesSize) { if (pricesSize==0 ||pricesSize==1 ) return 0 ; int *changes=(int *)malloc (sizeof (int )*(pricesSize-1 )); for (int i=0 ;i<pricesSize-1 ;i++) changes[i]=prices[i+1 ]-prices[i]; return findMaxSubarray(changes,0 ,pricesSize-2 )>0 ?findMaxSubarray(changes,0 ,pricesSize-2 ):0 ; }
2.树状数组:
引入辅助数组C,C[i]管辖的元素个数:\(2^k\) 个(k为i的二进制末尾0的个数).
求前缀和:求sum(A1+…+Am):查询的m转为二进制,不断消除当前末尾1,直至全部为0停止。
例:7(0111)[C7] -> 6(0110)[C6] -> 4(0100)[C4] -> 0.
故sum(A1+…+A7)=C4+C6+C7.
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 lowbit (int m) { return m&(-m); } int ans=0 ; int getSum (int m) { while (m>0 ) { ans+=C[m]; m-=lowbit(m); } } void update (int i,int value) { A[i]+=value; while (i<=n) { C[i]+=value; i+=lowbit(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 int lowbit[200010 ];int sum[200010 ]; int main () { for (int i=1 ;i<=200005 ;i++) lowbit[i]=i&(-i); int T,n,op; scanf ("%d" ,&T); while (T--) { scanf ("%d" ,&n); memset (sum,0 ,sizeof (int )*200010 ); long long ans=0 ; for (int i=0 ;i<n;i++) { scanf ("%d" ,&op); long long nowsum=0 ; for (int j=op;j>0 ;j-=lowbit[j]) nowsum+=sum[j]; ans+=i-nowsum; for (int j=op;j<=200000 ;j+=lowbit[j]) sum[j]++; } printf ("%lld\n" ,ans); } return 0 ; }
### 3.归并排序:O(n*lgn).
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 long long merge (int r[],int s[],int left,int mid,int right) { int i,j,k; long long num = 0 ; i=left; j=mid+1 ; k=left; while ((i<=mid)&&(j<=right)) { if (r[i]<=r[j]) s[k++] = r[i++]; else { num += j - k; s[k++]=r[j++]; } } while (i<=mid) s[k++]=r[i++]; while (j<=right) s[k++]=r[j++]; return num; } long long merge_sort (int r[],int s[],int left,int right) { long long ans = 0 ; int mid; int t[100010 ]; if (left==right) { s[left]=r[right]; return 0 ; } else { mid=(left+right)/2 ; ans+=merge_sort(r,t,left,mid); ans+=merge_sort(r,t,mid+1 ,right); ans+=merge(t,s,left,mid,right); } return ans; } int main () { int T; scanf ("%d" , &T); while (T--) { int n, a[100010 ]; scanf ("%d" , &n); for (int i = 0 ; i < n; i++) { scanf ("%d" , &a[i]); } printf ("%lld\n" , merge_sort(a,a,0 ,n-1 )); } }
## 堆 ### 1.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 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 void swap (int *a,int *b) { int tmp=*a; *a=*b; *b=tmp; } void max_heapify (int *A,int heapsize,int i) { int l=2 *i,r=2 *i+1 ,largest; if (l<=heapsize&&A[l]>A[i]) largest=l; else largest=i; if (r<=heapsize&&A[r]>A[largest]) largest=r; if (largest!=i) { swap(&A[i],&A[largest]); max_heapify(A,heapsize,largest); } } void build_maxHeap (int *A,int len) { int heapsize=len; for (int i=len/2 ;i>=1 ;i--) max_heapify(A,heapsize,i); } void heapSort (int *A,int len) { build_maxHeap(A,len); int heapsize=len; for (int i=len;i>=2 ;i--) { swap(&A[1 ],&A[i]); heapsize-=1 ; max_heapify(A,heapsize,1 ); } } int heap_extract_max (int *A,int *heapsize) { if (*heapsize<1 ) return -1 ; int max=A[1 ]; A[1 ]=A[*heapsize]; (*heapsize)--; max_heapify(A,*heapsize,1 ); return max; } void heap_increase_key (int *A,int i,int key) { if (A[i]>key) return ; A[i]=key; while (i>1 &&A[i/2 ]<A[i]) { swap(&A[i/2 ],&A[i]); i=i/2 ; } } #define INFINITY -10000 void maxHeap_insert (int *A,int key,int *heapsize) { (*heapsize)++; A[*heapsize]=INFINITY; heap_increase_key(A,*heapsize,key); } int A[100005 ];int main () { int n,op,x,heapsize=0 ;scanf ("%d" ,&n); for (int i=0 ;i<n;i++) { scanf ("%d" ,&op); if (op==1 ) { scanf ("%d" ,&x); maxHeap_insert(A,x,&heapsize); } else if (op==2 ) heap_extract_max(A,&heapsize); else if (op==3 ) printf ("%d\n" ,A[1 ]); } qsort(&A[1 ],heapsize,sizeof (int ),cmp); for (int i=1 ;i<=heapsize;i++) printf ("%d " ,A[i]); return 0 ; }
###
2.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 #include <queue> #include <functional> using namespace std ; priority_queue <int >p;priority_queue <int ,vector <int >,less<int >>p;priority_queue <int ,vector <int >,greater<int >>q;p.push(1 ); cout <<p.top(); p.pop(); if (p.empty()) struct Person { string name; int age; Person(const string &n,int a):name(n),age(a){} }; struct compareByAge {bool operator () (const Person& p1,const Person& p2) const { return p1.age>p2.age; } }; int main () { priority_queue <Person,vector <Person>,compareByAge>q; }
快速排序
性能分析
最坏划分:两子问题分别包括0和n-1个元素:T(n)=T(n-1)+θ(n),得T(n)=θ(n^2).
最好划分:T(n)=2T(n/2)+θ(n),得T(n)=θ(n lgn).
平衡划分:常数比例的划分,递归树深度为θ(lgn),每层工作量为O(n).总运行时间O(nlgn).
使用RANDOMIZED-PARTITION,在输入元素互异时,快排的期望运行时间为O(n lgn).
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 42 43 44 void quickSort (int list [],int left,int right,int *cnt) { if (left>=right) return ; int last=left; for (int i=left+1 ;i<=right;i++) { (*cnt)++; if (list [i]<list [left]) swap(&list [++last],&list [i]); } swap(&list [left],&list [last]); quickSort(list ,left,last-1 ,cnt); quickSort(list ,last+1 ,right,cnt); } 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=i+1 ; swap(&A[i],&A[j]); } } swap(&A[i+1 ],&A[r]); return i+1 ; } int Randomized_Partition (int *A,int p,int r) { int i=p+rand()%(r-p); swap(&A[i],&A[r]); return partition(A,p,r); } void quicksort (int *A,int p,int r) { if (p<r) { int q=Randomized_Partition(A,p,r); quicksort(A,p,q-1 ); quicksort(A,q+1 ,r); } }
求顺序统计量
求A[p,r]中第i小的元素--期望时间:θ(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 #include <stdio.h> #include <stdlib.h> void swap (int *a,int *b) { int tmp=*a; *a=*b; *b=tmp; } 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=i+1 ; swap(&A[i],&A[j]); } } swap(&A[i+1 ],&A[r]); return i+1 ; } int Randomized_Partition (int *A,int p,int r) { int i=p+rand()%(r-p); swap(&A[i],&A[r]); return partition(A,p,r); } int Randomized_Select (int *A,int p,int r,int i) { if (p==r) return A[p]; int q=Randomized_Partition(A,p,r); int k=q-p+1 ; if (i==k) return A[q]; else if (i<k) return Randomized_Select(A,p,q-1 ,i); else return Randomized_Select(A,q+1 ,r,i-k); } int main () { int A[10 ]={0 ,9 ,4 ,3 ,1 ,2 ,5 ,7 ,6 ,8 }; for (int i=1 ;i<=10 ;i++) printf ("%d " ,Randomized_Select(A,0 ,9 ,i)); return 0 ; }
线性时间排序
1.比较排序算法
O(n*lgn)时间内排序n个数:
归并排序,堆排序--最坏情况达到θ(n*lgn);
快速排序--平均情况达到θ(n*lgn).
排序算法下界:最坏情况下,任何比较排序算法都需要欧姆 次比较.
推论:堆排序,归并排序都是渐近最优的比较排序算法.
2.计数排序
1 2 3 4 5 6 7 8 9 10 11 12 13 void countSort (int *A,int *B,int n,int k) { int *C=(int *)malloc (sizeof (int )*(k+1 )); for (int i=0 ;i<=k;i++) C[i]=0 ; for (int j=0 ;j<n;j++) C[A[j]]+=1 ; for (int i=1 ;i<=k;i++) C[i]=C[i]+C[i-1 ]; for (int j=n-1 ;j>=0 ;j--) { B[C[A[j]]]=A[j]; C[A[j]]--; } }
滑动窗口
例:求序列的所有长度为k的连续子序列中,最大的数字种类数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const int N=1e5 +5 ;int n,k,cnt,ans,a[N];void solve () { cin >>n>>k; set <int > s; map <int ,int > m; for (int i=1 ;i<=n;i++){ cin >>a[i]; s.insert(a[i]); } if ((int )s.size!=k) {cout <<"-1" <<endl ;return ;} ans=0 ; for (int i=1 ;i<=n;i++){ m[a[i]]++; if (i>k) { m[a[i-k]]--; if (m[a[i-k]]==0 ) m.erase(a[i-k]); } ans=max(ans,(int )m.size()); } }
动态规划:
1.最优子结构:可由子问题最优解,构造原问题的最优解。(子问题无关,不共享资源,结果互不影响)
2.重叠子问题:反复求解相同的子问题,将解存入备忘录中。
(分治:每一步生成全新的子问题) ###
1.钢管切割:总长度固定,不同长度不同售价 r[n]=max(p[i]+r[n-i]).i=1~n.
C语言实现:θ(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 #define max(a,b) a>b?a:b #define INFINITY -2147483648 void print_cut_rod_solution (int *p,int n,int *s) { while (n>0 ) { printf ("%d " ,s[n]); n-=s[n]; } } int bottom_up_cut_rod (int *p,int n) { int *r=(int *)malloc (sizeof (int )*(n+1 )); int *s=(int *)malloc (sizeof (int )*(n+1 )); r[0 ]=0 ; for (int j=1 ;j<=n;j++) { int q=INFINITY; 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; } print_cut_rod_solution(p,n,s); return r[n]; } long long p[10005 ]; int main () { int n;scanf ("%d" ,&n); for (int i=1 ;i<=n;i++) scanf ("%lld" ,&p[i]); bottom_up_cut_rod(p,n); return 0 ; }
#### 变式:每次切割固定成本c
1 2 3 4 5 6 7 8 9 10 11 int modify_cut_rod (int *p,int n,int c) { int *r=(int *)malloc (sizeof (int )*(n+1 )); r[0 ]=0 ; for (int j=1 ;j<=n;j++) { int q=p[j]; for (int i=1 ;i<=j-1 ;i++) q=max(q,p[i]+r[j-i]-c); r[j]=q; } return r[n]; }
###
2.矩阵链乘法:求完全括号化方案,使得计算A1A2...An所需标量乘法次数最少.
括号化方案数量:卡特兰数 设:矩阵Ai的大小为:p[i-1]xp[i].
m[i,j]=min(m[i,k]+m[k+1,j]+p[i-1]p[k] p[j]),i<j. ####
(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 #define N 100 #define INFINITY_2 2147483647 int m[N][N],s[N][N]; int matrix_chain_order (int *p,int n) { for (int l=2 ;l<=n;l++) { for (int i=1 ;i<=n-l+1 ;i++) { int j=n-l+1 ; m[i][j]=INFINITY_2; for (int k=i;k<=j-1 ;k++) { int 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; } } } } return m[1 ][n]; }
#### (2)带备忘的自顶向下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int lookup_chain (int *p,int i,int j) { if (m[i][j]<INFINITY_2) return m[i][j]; if (i==j) m[i][j]=0 ; else { for (int k=i;k<=j-1 ;k++) { int q=lookup_chain(p,i,k)+lookup_chain(p,k+1 ,j)+p[i-1 ]*p[k]*p[j]; if (q<m[i][j]) m[i][j]=q; } } return m[i][j]; } int memorized_matrix_chain (int *p,int n) { for (int i=1 ;i<=n;i++) { for (int j=i;j<=n;j++) m[i][j]=INFINITY_2; } return lookup_chain(p,1 ,n); }
####
构造最优解: 1 2 3 4 5 6 7 8 void print_optimal_parens (int **s,int i,int j) { if (i==j) {printf ("A%d" ,i);return ;} printf ("(" ); print_optimal_parens(s,i,s[i][j]); print_optimal_parens(s,s[i][j]+1 ,j); printf (")" ); }
3.流水线调度
dp[i][j]:在流水线上完成第j个步骤时的最小时间。 for(int
k=0;k<3;k++) dp[i][j] =
min(dp[i][j],(j>=1?dp[k][j-1]+t[k][i]:0LL)+p[i][j]); 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 ; #define MAXN 12 #define MAXM 22 typedef long long ll;const ll inf = 1e18 ;int a[MAXN][MAXM], t[MAXN][MAXN];ll dp[MAXN][MAXM],nxt[MAXN][MAXM]; int main () { ios::sync_with_stdio(false ); cin .tie(0 ); int tt; cin >> tt; while (tt--) { int m,n;cin >>m>>n; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cin >> a[i][j]; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) cin >> t[i][j]; for (int j = m; j >= 1 ; j--) { for (int i = 1 ; i <= n; i++) { dp[i][j] = inf; for (int k = 1 ; k <= n; k++) { ll p=(j<=m-1 ?dp[k][j+1 ]+t[i][k]:0LL )+a[i][j]; if (p < dp[i][j]) { dp[i][j] = p; nxt[i][j] = k; } } } } ll ans=dp[1 ][1 ],cur=1 ; for (int i=1 ;i<=n;i++) { if (ans>dp[i][1 ]) {ans=dp[i][1 ];cur=i;} } cout <<ans<<"\n" ; for (int i=1 ;i<=m;i++) { cout <<"Station" <<i<<": Line" <<cur<<"\n" ; cur = nxt[cur][i]; } } return 0 ; }
### 4.OBST(最优二叉搜索树)--区间dp
关键字的升序序列:K=<k1,...,kn>,有:k1<...<kn.--概率pi.(内部节点)
n+1个伪关键字:d0,...,dn.有:ki<di<k(i+1).--概率qi.(叶节点)
有:sum(pi)+sum(qi)=1.
搜索代价:E=sum[(depth(ki)+1)*pi]+sum[(depth(di)+1)*qi]
=1+sum[depth(ki)*pi]+sum[depth(di)*qi].
设:e[i,j]表示:在包含关键字ki,...,kj的OBST中搜索一次的期望代价.
w[i,j]表示:包含关键字ki,...,kj的子树,概率之和:
w[i,j]=sum(pl)(l=ij)+sum(ql)(l=i-1 j).
root[i,j]表示包含关键字ki,...,kj的子树的根.
递归公式:e[i,j]=q[i-1] if j==i-1.
=min{e[i,r-1]+e[r+1,j]+w(i,j)}(i<=r<=j) if i<=j.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #define inf 2147483647 int e[MAXN+1 ][MAXN+1 ],w[MAXN+1 ][MAXN+1 ],root[MAXN+1 ][MAXN+1 ];void OBST (int *p,int *q,int n) { for (int i=1 ;i<=n+1 ;i++) {e[i][i-1 ]=w[i][i-1 ]=q[i-1 ];} for (int l=1 ;l<=n;l++) { for (int i=1 ;i<=n-l+1 ;i++) { int j=i+l-1 ;e[i][j]=inf; w[i][j]=w[i][j-1 ]+p[j]+q[j]; for (int r=i;r<=j;r++) { int t=e[i][r-1 ]+e[r+1 ][j]+w[i][j]; if (t<e[i][j]){e[i][j]=t;root[i][j]=r;} } } } }
5.最长公共子序列(LCS)--O(n^2)
c[i][j]=0. if i==0||j==0. c[i-1][j-1]+1. if
i,j>0&&x[i]=y[j]. max(c[i][j-1],c[i-1][j]). if
i,j>0&&x[i]!=y[j].
θ(mn)个子问题. 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 #define max_M 100 #define max_N 100 int c[max_M+1 ][max_N+1 ],b[max_M][max_N],a[max_M+1 ][max_N+1 ]; void print_LCS (char *X,int i,int j) { if (i==0 ||j==0 ) return ; if (b[i][j]==-1 ) { print_LCS(X,i-1 ,j-1 ); printf ("%c" ,X[i]); } else if (b[i][j]==0 ) print_LCS(X,i-1 ,j); else print_LCS(X,i,j-1 ); } int LCS_length (char *X,char *Y,int m,int n) { for (int i=1 ;i<=m;i++) { for (int j=1 ;j<=n;j++) { if (X[i]==Y[j]) { c[i][j]=c[i-1 ][j-1 ]+1 ;b[i][j]=-1 ; a[i][j]=a[i-1 ][j-1 ]+1 ; } else if (c[i-1 ][j]>=c[i][j-1 ]) { c[i][j]=c[i-1 ][j];b[i][j]=0 ; a[i][j]=0 ; } else { c[i][j]=c[i][j-1 ];b[i][j]=1 ; a[i][j]=0 ; } } } print_LCS(X,m,n); return c[m][n]; } char A[2005 ],B[2005 ],C[2005 ],D[2005 ];int main () { int T;scanf ("%d" ,&T);getchar(); while (T--){ memset (A,'\0' ,sizeof (char )*2005 ); memset (B,'\0' ,sizeof (char )*2005 ); memset (C,'\0' ,sizeof (char )*2005 ); memset (D,'\0' ,sizeof (char )*2005 ); memset (c,0 ,sizeof (int )*2006 *2006 ); memset (b,0 ,sizeof (int )*2006 *2006 ); ans1=0 ;ans2=0 ; fgets(A,2003 ,stdin ); fgets(B,2003 ,stdin ); int lenA=strlen (A),lenB=strlen (B); if (A[lenA-1 ]=='\n' ) { A[lenA-1 ]='\0' ; lenA--; } if (B[lenB-1 ]=='\n' ) { B[lenB-1 ]='\0' ; lenB--; } strcpy (&C[1 ],A);strcpy (&D[1 ],B); LCS_length(C,D,lenA,lenB); printf ("%d %d\n" ,ans2,ans1); } ans1=c[m][n]; for (int i=1 ;i<=m;i++){ for (int j=1 ;j<=n;j++){ if (a[i][j]>ans2) ans2=a[i][j]; } } return 0 ; }
### 6.最长上升子序列(LIS)--O(n*logn)
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 LIS (vector <int > &nums) { int len=1 ,n=(int )nums.size(); if (n==0 ) return 0 ; vector <int > d (n+1 ,0 ) ; d[len]=nums[0 ]; for (int i=1 ;i<n;i++) { if (nums[i]>d[len]) d[++len]=nums[i]; else { int l=1 ,r=len,pos=0 ; while (l<=r) { int mid=(l+r)>>1 ; if (d[mid]<nums[i]) {pos=mid;l=mid+1 ;} else r=mid-1 ; } d[pos+1 ]=nums[i]; } } return len; }
拓展:最长公共上升子序列 思路:f[i][j]表示:所有a[1 ~
i]和b[1 ~ j]中以b[j]结尾的公共上升子序列的集合. 1 2 3 4 5 6 7 8 9 10 for (int i = 1 ; i <= m; ++ i) { int maxv = 1 ; for (int j = 1 ; j <= n; ++ j) { f[i][j] = f[i - 1 ][j]; if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv); if (a[i] > b[j]) maxv = max(maxv, f[i - 1 ][j] + 1 ); } } int ans = 0 ; for (int i = 1 ; i <= n; ++ i) ans = max(f[n][i], ans);
7.背包问题
(1)0-1背包:一种物品使用一次
压缩为一维:f[j]表示:背包容量不超过j时的最大价值. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <bits/stdc++.h> using namespace std ; const int N=1005 ;int f[N],v[N],w[N]; int main () { int n,m; cin >>n>>m; for (int i=1 ;i<=n;i++) cin >>v[i]>>w[i]; for (int i=1 ;i<=n;i++) for (int j=m;j>=v[i];j--) f[j] = max(f[j],f[j-v[i]]+w[i]); cout <<f[m]<<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 40 41 42 43 typedef struct { int val; int weight; }goods; #define N 1005 goods all[N+1 ]; int max (int a,int b) { if (a>b) return a; else return b; } int Knapsack (int n,int W) { int **K=(int **)malloc (sizeof (int *)*(n+1 )); for (int i=0 ;i<=n;i++) K[i]=(int *)malloc (sizeof (int )*(W+1 )); for (int i=1 ;i<=n;i++) K[i][0 ]=0 ; for (int j=1 ;j<=W;j++) K[0 ][j]=0 ; for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=W;j++) { if (j<all[i].weight) K[i][j]=K[i-1 ][j]; else K[i][j]=max(K[i-1 ][j],K[i-1 ][j-all[i].weight]+all[i].val); } } return K[n][W]; } int main () { int T,n,V;scanf ("%d" ,&T); while (T--){ scanf ("%d%d" ,&n,&V); for (int i=1 ;i<=n;i++) scanf ("%d%d" ,&all[i].val,&all[i].weight); printf ("%d\n" ,Knapsack(n,V)); } return 0 ; }
#### (2)完全背包:每种物品可使用无限次 与0-1背包对比:
二维:f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]); //01背包 f[i][j] =
max(f[i][j],f[i][j-v[i]]+w[i]);//完全背包 一维:如代码. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <bits/stdc++.h> using namespace std ; const int N=1005 ;int f[N],v[N],w[N]; int main () { int n,m; cin >>n>>m; 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<=m;j++) f[j] = max(f[j],f[j-v[i]]+w[i]); cout <<f[m]<<endl ; return 0 ; }
#### (3)多重背包:每件物品最多有si件. 无法优化为一维. f[i][j] =
max(f[i][j], f[i - 1][j - k * v[i]] + k * w[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 #include <bits/stdc++.h> using namespace std ; const int N = 110 ;int v[N],w[N],s[N];int f[N][N];int main () { int n,m; cin >>n>>m; for (int i=1 ;i<=n;i++) cin >>v[i]>>w[i]>>s[i]; for (int i=1 ;i<=n;i++) for (int j=0 ;j<=m;j++) { for (int k=0 ;k*v[i]<=j&&k<=s[i];k++) f[i][j] = max(f[i][j],f[i-1 ][j-k*v[i]]+k*w[i]); } cout <<f[n][m]<<endl ; return 0 ; }``` #### (4 )分组背包:同一组内物品最多选一个 ```C #include <bits/stdc++.h> using namespace std ; const int N=110 ;int v[N][N],w[N][N];int s[N];int f[N];int main () { int n,m; cin >>n>>m; for (int i=1 ;i<=n;i++) { cin >>s[i]; for (int j=0 ;j<s[i];j++) cin >>v[i][j]>>w[i][j]; } for (int i=1 ;i<=n;i++) for (int j=m;j>=0 ;j--) for (int k=0 ;k<s[i];k++) if (v[i][k]<=j) f[j]=max(f[j],f[j-v[i][k]]+w[i][k]); cout <<f[m]<<endl ; return 0 ; }
8.一些记录
长高问题:dp[0..k][0..n],dp[i][j]为:完成第j个点时,跳过i次深坑,得到的最大身高(应有:i<=j)
贪心
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 #define N 100005 typedef struct { long long start; long long finish; }act; act all[N]; act ans[N]; int n; int cnt; int cmp (const void *e1,const void *e2) { act *p=(act *)e1,*q=(act *)e2; return p->finish-q->finish; } void recursive_activity_selector (int k,int n) { int m=k+1 ; while (m<=n&&all[m].start<all[k].finish) m=m+1 ; if (m<=n) { ans[cnt++]=all[m]; recursive_activity_selector(m,n); } else return ; } int main () { int T,n;scanf ("%d" ,&T); while (T--){ cnt=0 ; scanf ("%d" ,&n); for (int i=1 ;i<=n;i++) {scanf ("%lld%lld" ,&all[i].start,&all[i].finish);all[i].finish+=all[i].start;} qsort(&all[1 ],n,sizeof (act),cmp); recursive_activity_selector(0 ,n); printf ("%d\n" ,cnt); } return 0 ; }
2.Huffman编码
变长编码,每个字符用唯一的一个二进制串表示(性质:没有任何码字是其他码字的前缀)
问题:构造Huffman树,使得:B(T)=c.freqd(c)最小,c.freq为点权重,d(c)为点深度
贪心算法构造:n-1次合并,每次合并时:最小优先队列(最小堆实现)选取最小两个元素,合并为一个元素,插入队列--O(n lgn)
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 #include <bits/stdc++.h> #define maxn 200086 using namespace std ; typedef long long ll;int n;priority_queue <ll, vector <ll>, greater<ll> > q; int main () { scanf ("%d" , &n); for (int i = 1 ;i <= n;i++){ int x; scanf ("%d" , &x); q.push(x); } ll ans = 0 ; for (int i = 1 ;i < n;i++){ ll x = q.top();q.pop(); x += q.top();q.pop(); ans += x * 2 ; q.push(x); } printf ("%lld" , ans); }
### 3.合法括号序列 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 char S[100005 ];int l,r,no;int left,right; bool judge (int n) { if (n%2 ) return false ; for (int i=0 ;i<n;i++) { if (S[i]=='(' ) l++; else if (S[i]==')' ) r++; else no++; } if (l+no<r||r+no<l) return false ; l=n/2 -l;r=n/2 -r; for (int i=0 ;i<n;i++) { if (left<=right&&left!=0 ) return false ; if (S[i]=='(' ) left++; else if (S[i]==')' ) right++; else { if (left<n/2 &&l>0 ) { S[i]='(' ;left++;l--; } else { S[i]=')' ;right++;r--; } } } return true ; } int main () { int n;scanf ("%d" ,&n); scanf ("%s" ,S); int len=strlen (S); if (S[len-1 ]=='\n' ) {S[len-1 ]='\0' ;len--;} if (!judge(n)) printf (":(" ); else printf ("%s" ,S); return 0 ; }
4.求不超过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 52 53 54 55 56 #include <cstdio> #include <algorithm> #include <cstring> using namespace std ; const int N = 505 ;char a[N], b[N];int n, mid;int leq () { for (int i = 1 ; i <= n; i++){ if (b[i] < a[i]) return 1 ; if (b[i] > a[i]) return 0 ; } return 1 ; } void solve () { scanf ("%s" , a + 1 ); n = strlen (a + 1 ); mid = 1 ; for (int i = 1 , j = n; i <= j; i++, j--) { b[i] = b[j] = a[i]; mid = i; } b[n + 1 ] = 0 ; if (leq()) { printf ("%s\n" , b + 1 ); return ; } b[mid]--; for (int i = mid; i && b[i] < '0' ; i--) { b[i] += 10 ; b[i - 1 ]--; } for (int i = 1 ; i <= mid; i++) b[n - i + 1 ] = b[i]; if (b[1 ] != '0' && leq()) { printf ("%s\n" , b + 1 ); return ; } for (int i = 1 ; i < n; i++) b[i] = '9' ; b[n] = 0 ; printf ("%s\n" , b + 1 ); } int main () { int T; scanf ("%d" , &T); while (T--) solve(); return 0 ; }
图论
1.DFS
例: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 #include <iostream> using namespace std ; #define ll long long int n;ll pw[10 ],cnt[10 ],bit[10 ],ans; int check (ll s) { for (int i=0 ;i<10 ;i++) bit[i]=0 ; while (s>0 ){ bit[s%10 ]++; s/=10 ; } for (int i=0 ;i<10 ;i++){ if (cnt[i]!=bit[i]) return 0 ; } return 1 ; } void dfs (int bound,ll num,ll limit) { if (num<limit/10 ) return ; ans+=check(num)*num; for (int i=0 ;i<=bound;i++){ cnt[i]++; dfs(i,num+pw[i],limit*10 ); cnt[i]--; } } int main () { ios::sync_with_stdio(false ); cin .tie(0 );cout .tie(0 ); int T;cin >>T; while (T--){ cin >>n; ans=0 ; for (int i=0 ;i<10 ;i++){ cnt[i]=0 ;pw[i]=1 ; for (int j=0 ;j<n;j++) pw[i]*=i; } dfs(9 ,0 ,1 ); cout <<ans<<endl ; } return 0 ; }
2.BFS
例:地图中每个格子,有可向上下左右移动的步数,求(1,1)->(n,m)的最小次数.
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> #include <array> #define ll long long using namespace std ; int n,m;ll calc (ll x,ll step,ll n,int neg) { if (n==1 ) return x; if (neg){ ll a=(x-step%(2 *n-2 )+(n-2 )+(2 *n-2 ))%(2 *n-2 ); if (a>=n-2 ) return a-(n-2 ); else return (n-2 )-a; }else { ll a=(x+step%(2 *n-2 ))%(2 *n-2 ); if (a>=n) return 2 *n-2 -a; else return a; } } int main () { ios::sync_with_stdio(false ); cin .tie(0 ); int tt;cin >>tt; while (tt--){ cin >>n>>m; vector <vector <int >>a (n,vector <int >(m)); vector <vector <bool >>vis (n,vector <bool >(m)); for (int i=0 ;i<n;i++){ for (int j=0 ;j<m;j++) cin >>a[i][j]; } queue <array <ll,3 >>q; vis[0 ][0 ]=true ; q.push({0 ,0 ,0 }); ll ans=-1 ; while (!q.empty()){ ll r=q.front()[0 ],c=q.front()[1 ],w=q.front()[2 ]; q.pop(); if (r==n-1 &&c==m-1 ) {ans=w;break ;} int step=a[r][c]; ll nr,nc; nr=calc(r,step,n,0 );nc=c; if (!vis[nr][nc]) {vis[nr][nc]=true ;q.push({nr,nc,w+1 });} nr=calc(r,step,n,1 );nc=c; if (!vis[nr][nc]) {vis[nr][nc]=true ;q.push({nr,nc,w+1 });} nr=r;nc=calc(c,step,m,0 ); if (!vis[nr][nc]) {vis[nr][nc]=true ;q.push({nr,nc,w+1 });} nr=r;nc=calc(c,step,m,1 ); if (!vis[nr][nc]) {vis[nr][nc]=true ;q.push({nr,nc,w+1 });} } cout <<ans<<'\n' ; } }
3.单源最短路问题
(1)Bellford:可能包括负环
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 #define N 2005 #define M 6005 #define inf 1e9 #define min(a,b) a<b?a:b struct edge { int u,v,w; }; struct edge Edges [M ];int dis[N]; int n,m; bool BellFord (int s) { fill(dis,dis+n+1 ,inf); dis[s]=0 ; for (int i=1 ;i<n;i++) { for (int j=1 ;j<=m;j++) { dis[Edges[j].v]=min(dis[Edges[j].v],dis[Edges[j].u]+Edges[j].w); } } for (int j=1 ;j<=m;j++) { if (dis[Edges[j].v]>dis[Edges[j].u]+Edges[j].w) return false ; } return true ; } int main () {#ifdef abyss freopen("in.txt" , "r" , stdin ); #endif ios::sync_with_stdio(false ); cin .tie(0 ); cout .tie(0 ); int T;cin >>T; while (T--){ cin >>n>>m; for (int i=1 ;i<=m;i++) cin >>Edges[i].u>>Edges[i].v>>Edges[i].w; bool ans=BellFord(1 ); if (ans==false ) cout <<"boo how\n" ; else { for (int i=1 ;i<=n;i++) cout <<dis[i]<<(i==n?"\n" :" " ); } } return 0 ; }
(2)Dijkstra算法:无向图,无负环
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 #include <iostream> #include <vector> #include <queue> #include <algorithm> #include <cstring> using namespace std ; #define inf 1e18 class Dijkstra {public: int n; vector <vector <pair <int ,long long >>>g; vector <long long >dis; Dijkstra(int k):n(k){ g.resize(n+1 ); dis.resize(n+1 ,inf); } void add (int u,int v,long long w) { g[u].emplace_back(v,w); } void solve (int s) { vector <bool >vis(n+1 ); priority_queue <pair <long long ,int >,vector <pair <long long ,int >>,greater<pair <long long ,int >>>q; for (int i=1 ;i<=n;i++) dis[i]=inf; dis[s]=0 ; q.push({0 ,s}); while (!q.empty()){ int u=q.top().second; q.pop(); if (vis[u]) continue ; vis[u]=true ; for (auto [v,w]:g[u]){ if (dis[v]>dis[u]+w) dis[v]=dis[u]+w; q.push({dis[v],v}); } } } }; class Dijkstra2 {public: int n; vector <int >head; vector <int >nxt; vector <pair <int ,long long >>to; vector <long long >dis; Dijkstra2(int k):n(k){ head.resize(n+1 ,-1 ); dis.resize(n+1 ,inf); } void add (int u,int v,long long w) { int cnt=nxt.size(); to.emplace_back(v,w); nxt.push_back(head[u]); head[u]=cnt; } void solve (int s) { vector <bool >vis(n+1 ); priority_queue <pair <long long ,int >,vector <pair <long long ,int >>,greater<pair <long long ,int >>>q; for (int i=1 ;i<=n;i++) dis[i]=inf; dis[s]=0 ; q.push({0 ,s}); while (!q.empty()){ int u=q.top().second; q.pop(); if (vis[u]) continue ; vis[u]=true ; for (int i=head[u];i!=-1 ;i=nxt[i]) { auto [v,w]=to[i]; if (dis[v]>dis[u]+w) dis[v]=dis[u]+w; q.push({dis[v],v}); } } } };
4.所有点间的最短路问题--Floyd
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 #define N 305 #define M 100005 #define inf 1e18 int n,m,q;long long dis[N][N]; int u,v;long long w;void floyd () { for (int k=1 ;k<=n;k++) { for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=n;j++) { dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } } } } int main () { cin >>n>>m; for (int i=0 ;i<=n;i++) { for (int j=0 ;j<=n;j++) dis[i][j]=inf; dis[i][i]=0 ; } for (int i=0 ;i<m;i++) { cin >>u>>v>>w; dis[u][v]=min(dis[u][v],w); } floyd(); cin >>q; for (int i=0 ;i<q;i++) { cin >>u>>v; if (dis[u][v]>inf/2 ) cout <<"-1\n" ; else cout <<dis[u][v]<<"\n" ; } return 0 ; }
5.拓扑排序
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 #include <iostream> #include <algorithm> #include <queue> using namespace std ; #define N 100005 #define M 400005 int n,m,din[N];vector <int > e[M];priority_queue <int > q;int main () { int u,v;cin >>n>>m; for (int i=0 ;i<m;i++) { cin >>u>>v; e[u].push_back(v); din[v]+=1 ; } for (int i=1 ;i<=n;i++) { if (din[i]==0 ) q.push(i); } while (!q.empty()) { int u=q.top();q.pop(); cout <<u<<" " ; for (auto v:e[u]) { din[v]-=1 ; if (din[v]==0 ) q.push(v); } } 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 71 72 73 74 75 76 77 78 79 80 81 82
6.最小生成树--Kruskal:O(m*logm)
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 #include <bits/stdc++.h> using namespace std ; class dsu { public: int n; vector <int > p; dsu(int _n) : n(_n) { p.resize(n); iota(p.begin(), p.end(), 0 ); } inline int find (int x) { return (x == p[x] ? x : (p[x] = find(p[x]))); } inline bool unite (int x, int y) { x = find(x); y = find(y); if (x != y) { p[x] = y; return true ; } return false ; } }; int main () { ios::sync_with_stdio(false ); cin .tie(0 ); int tt; cin >> tt; while (tt--) { int n, m; cin >> n >> m; vector <array <int , 3 >> e(m); for (int i = 0 ; i < m; i++) { int u, v, w; cin >> u >> v >> w; --u; --v; e[i] = {u, v, w}; } vector <int > p (m) ; iota(p.begin(), p.end(), 0 ); sort(p.begin(), p.end(), [&](int u, int v) { return e[u][2 ] < e[v][2 ]; }); dsu d = dsu(n); long long ans = 0 ; for (int i : p) { int u = e[i][0 ], v = e[i][1 ]; if (d.unite(u, v)) { ans += e[i][2 ]; } } cout << ans << '\n' ; } return 0 ; }
7.网络流--Dinic算法
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 #include <queue> #include <iostream> using namespace std ; const long long inf=2005020600 ;int n,m,s,t,u,v;long long w,ans,dis[105 ];int tot=1 ,now[105 ],head[105 ];struct node { int to,net; long long val; } e[10010 ]; inline void add (int u,int v,long long w) { e[++tot].to=v; e[tot].val=w; e[tot].net=head[u]; head[u]=tot; e[++tot].to=u; e[tot].val=0 ; e[tot].net=head[v]; head[v]=tot; } inline int bfs () { for (int i=1 ;i<=n;i++) dis[i]=inf; queue <int > q; q.push(s); dis[s]=0 ; now[s]=head[s]; while (!q.empty()) { int x=q.front(); q.pop(); for (int i=head[x];i;i=e[i].net) { int v=e[i].to; if (e[i].val>0 &&dis[v]==inf) { q.push(v); now[v]=head[v]; dis[v]=dis[x]+1 ; if (v==t) return 1 ; } } } return 0 ; } inline long long dfs (int x,long long sum) { if (x==t) return sum; long long k,res=0 ; for (int i=now[x];i&∑i=e[i].net) { now[x]=i; int v=e[i].to; if (e[i].val>0 &&(dis[v]==dis[x]+1 )) { k=dfs(v,min(sum,e[i].val)); if (k==0 ) dis[v]=inf; e[i].val-=k; e[i^1 ].val+=k; res+=k; sum-=k; } } return res; } int main () { int T;scanf ("%d" ,&T); while (T--){ scanf ("%d%d%d%d" ,&n,&m,&s,&t); for (int i=1 ;i<=m;i++) { scanf ("%d%d%lld" ,&u,&v,&w); add(u,v,w); } ans=0 ; while (bfs()) { ans+=dfs(s,inf); } printf ("%lld\n" ,ans); tot=1 ;ans=0 ; for (int i=0 ;i<=n;i++) now[i]=head[i]=0 ; } return 0 ; }
8.二分图匹配:O(n^3)
(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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <iostream> #include <vector> #include <algorithm> #include <queue> using namespace std ; int n;class HK {public: int n; vector <vector <int >> g; vector <int >match; vector <int >vis; HK(int _n):n(_n),g(_n+1 ),match(_n+1 ),vis(_n+1 ){} void add (int u,int v) {g[u].push_back(v);} bool find (int x) { for (int i:g[x]) { if (!vis[i]) { vis[i]=true ; if (!match[i]||find(match[i])) { match[i]=x; return true ; } } } return false ; } int solve () { int res=0 ; for (int i=1 ;i<=n;i++) { fill(vis.begin(),vis.end(),false ); if (find(i)) res+=1 ; } return res; } }; struct { int first,second; }male[405 ],female[405 ]; int main () { ios::sync_with_stdio(false ); cin .tie(0 ); cin >>n; HK k (n) ; for (int i=1 ;i<=n;i++) cin >>male[i].first; for (int i=1 ;i<=n;i++) cin >>male[i].second; for (int i=1 ;i<=n;i++) cin >>female[i].first; for (int i=1 ;i<=n;i++) cin >>female[i].second; for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=n;j++) { if (male[i].first>=female[j].second&&female[j].first>=male[i].second) k.add(i,j); } } cout <<k.solve()<<endl ; 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 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 #include <iostream> #include <vector> #include <algorithm> #include <cstring> #define ll long long #define ul unsigned long long #define pr pair<ll,ll> #define MAX 250 #define inf 0x3f3f3f3f3f using namespace std ; int n; ll x,y; pr from[MAX],to[MAX]; int match[MAX]; int va[MAX],vb[MAX]; ll w[MAX][MAX]; ll la[MAX],lb[MAX]; ll slack[MAX]; void init () { memset (w,0xbf ,sizeof (w)); for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=n;j++) w[i][j]=-abs (from[i].first-to[j].first)-abs (from[i].second-to[j].second); } } bool dfs (int x) { va[x]=1 ; for (int y=1 ;y<=n;y++) { if (vb[y]) continue ; ll t=la[x]+lb[y]-w[x][y]; if (t==0 ) { vb[y]=1 ; if (match[y]==-1 ||dfs(match[y])) { match[y]=x; return true ; } } else slack[y]=min(slack[y],t); } return false ; } ll KM () { memset (match,-1 ,sizeof (match)); memset (lb,0 ,sizeof (lb)); for (int i=1 ;i<=n;i++) { la[i]=-inf; for (int j=1 ;j<=n;j++) la[i]=max(la[i],w[i][j]); } for (int i=1 ;i<=n;i++) { memset (slack,0x3f ,sizeof (slack)); while (true ) { memset (va,0 ,sizeof (va)); memset (vb,0 ,sizeof (vb)); if (dfs(i)) break ; ll d=inf; for (int j=1 ;j<=n;j++) { if (!vb[j]) d=min(d,slack[j]); } for (int j=1 ;j<=n;j++) { if (va[j]) la[j]-=d; if (vb[j]) lb[j]+=d; } } } ll ans=0 ; for (int i=1 ;i<=n;i++) ans+=w[match[i]][i]; return ans; } int main () { int t;cin >>t; while (t--){ cin >>n; for (int i=1 ;i<=n;i++) { cin >>x>>y; from[i]=make_pair (x,y); } for (int i=1 ;i<=n;i++) { cin >>x>>y; to[i]=make_pair (x,y); } init(); cout <<-KM()<<endl ; } return 0 ; }
计算几何
1.判断三点是否共线(三维)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct Point { double x, y, z; }; bool areThreePointsCollinear (struct Point p1, struct Point p2, struct Point p3) { struct Point v1 = {p2.x - p1.x, p2.y - p1.y, p2.z - p1.z}; struct Point v2 = {p3.x - p2.x, p3.y - p2.y, p3.z - p2.z}; double crossProduct[3 ] = {v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x}; return fabs (crossProduct[0 ]) < 1e-9 && fabs (crossProduct[1 ]) < 1e-9 && fabs (crossProduct[2 ]) < 1e-9 ; }
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 typedef long long ll;typedef struct { ll x,y; }P; ll direction (P pi,P pj,P pk) { return (pk.x-pi.x)*(pj.y-pi.y)-(pj.x-pi.x)*(pk.y-pi.y); } bool on_Segment (P pi,P pj,P pk) { if (min(pi.x,pj.x)<=pk.x&&pk.x<=max(pi.x,pj.x)&&min(pi.y,pj.y)<=pk.y&&pk.y<=max(pi.y,pj.y)) return true ; else return false ; } bool parallel (P p1,P p2,P p3,P p4) { if ((p2.y-p1.y)*(p4.x-p3.x)==(p4.y-p3.y)*(p2.x-p1.x)) return true ; else return false ; } void Segments_Intersect (P p1,P p2,P p3,P p4) { ll d1=direction(p3,p4,p1); ll d2=direction(p3,p4,p2); ll d3=direction(p1,p2,p3); ll d4=direction(p1,p2,p4); if (((d1>0 &&d2<0 )||(d1<0 &&d2>0 ))&&((d3>0 &&d4<0 )||(d3<0 &&d4>0 ))) cout <<"intersect\n" ; else if (d1==0 &&on_Segment(p3,p4,p1)) cout <<"intersect\n" ; else if (d2==0 &&on_Segment(p3,p4,p2)) cout <<"intersect\n" ; else if (d3==0 &&on_Segment(p1,p2,p3)) cout <<"intersect\n" ; else if (d4==0 &&on_Segment(p1,p2,p4)) cout <<"intersect\n" ; else if (parallel(p1,p2,p3,p4)) cout <<"parallel\n" ; else cout <<"neither\n" ; } P p1,q1,p2,q2; int main () { int t;cin >>t; while (t--){ cin >>p1.x>>p1.y>>q1.x>>q1.y; cin >>p2.x>>p2.y>>q2.x>>q2.y; Segments_Intersect(p1,q1,p2,q2); } return 0 ; }
3.求点到线段最小距离
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 struct Pt { double x,y; }; struct Segment { Pt p1,p2; }; double getDis (Pt p1,Pt p2) { return sqrt (pow (p2.x-p1.x,2 )+pow (p2.y-p1.y,2 )); } double disToSegment (Pt p,Segment l) { double len=getDis(l.p1, l.p2); if (len==0 ) { return getDis(p, l.p1); } double v = ((p.x - l.p1.x) * (l.p2.x - l.p1.x) + (p.y - l.p1.y) * (l.p2.y - l.p1.y)) / pow (len, 2 ); if (v <= 0 ) { return getDis(p, l.p1); } else if (v >= 1 ) { return getDis(p, l.p2); } else { Pt u={ l.p1.x+v*(l.p2.x-l.p1.x),l.p1.y+v*(l.p2.y-l.p1.y)}; return getDis(p,u); } } double xa,ya,xp,yp,xq,yq;int main () { int t;cin >>t; while (t--){ cin >>xa>>ya>>xp>>yp>>xq>>yq; Pt p ={xa,ya}; Segment l = {{xp,yp},{xq,yq}}; printf ("%.3f\n" ,disToSegment(p,l)); } return 0 ; }
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 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 #include <iostream> #include <cstdio> #include <vector> #include <algorithm> using namespace std ; struct Vec { long long x, y; Vec() {} Vec(long long x, long long y) { this->x = x; this->y = y; } long long len2 () const { return x * x + y * y; } void read () { scanf ("%lld%lld" , &x, &y); } void print () { printf ("%lld %lld\n" , x, y); } }; typedef Vec Point;Vec operator + (const Vec &a, const Vec &b) { return Vec(a.x + b.x, a.y + b.y); } Vec operator - (const Vec &a, const Vec &b) { return Vec(a.x - b.x, a.y - b.y); } Vec operator * (long long a, const Vec &b) { return Vec(a * b.x, a * b.y); } long long operator * (const Vec &a, const Vec &b) { return a.x * b.y - b.x * a.y; }long long operator ^ (const Vec &a, const Vec &b) { return a.x * b.x + a.y * b.y; }typedef vector <Point> Polygon;typedef Polygon Points;bool onleft (const Vec &a, const Vec &b) { return a * b < 0 ; } bool onright (const Vec &a, const Vec &b) { return a * b > 0 ; }Polygon convex (Points p) { int sz = p.size(); sort(p.begin(), p.end(), [&](const Point &a, const Point &b) { return a.x != b.x ? a.x < b.x : a.y < b.y; }); Polygon c (p.size() + 1 ) ; int n = 0 ; for (int i = 0 ; i < sz; i++) { while (n > 1 && !onleft(p[i] - c[n - 2 ], c[n - 1 ] - c[n - 2 ])) n--; c[n++] = p[i]; } int t = n; for (int i = sz - 1 ; i >= 0 ; i--) { while (n > t && !onleft(p[i] - c[n - 2 ], c[n - 1 ] - c[n - 2 ])) n--; c[n++] = p[i]; } c.resize(--n); return c; } long long areadb (Polygon &p) { long long r = 0 ; int n = p.size(); for (int i = 0 ; i < n; i++) r += (p[i] - p[0 ]) * (p[(i + 1 ) % n] - p[i]); return r; } long long diameter2 (Polygon &p) { long long r = 0 ; int n = p.size(); int a = 0 ; for (int i = 0 ; i < n; i++) { Vec e = p[(i + 1 ) % n] - p[i]; while (onleft(p[(a + 1 ) % n] - p[a % n], e) || a == i) { r = max({r, (p[i] - p[a]).len2(), (p[(i + 1 ) % n] - p[a]).len2()}); a = (a + 1 ) % n; } r = max({r, (p[i] - p[a]).len2(), (p[(i + 1 ) % n] - p[a]).len2()}); } return r; } int n;Points p; void solve () { scanf ("%d" , &n); p.resize(n); for (int i = 0 ; i < n; i++) p[i].read(); p = convex(p); long long r = areadb(p); printf ("%lld.%lld\n" , r / 2 , (r & 1 ) * 5 ); } int main () { int T; scanf ("%d" , &T); while (T--) solve(); return 0 ; }
FFT
1.DFT模版--大数乘法
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 #include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #define N (1<<18)+5 const double pi=acosl(-1.0 );using namespace std ; struct complex { double r,i; complex (){r=0 ;i=0 ;} complex (double re,double im){r=re;i=im;} double len2 () const {return r*r+i*i;} complex bar () const {return complex (r,-i);} }; complex operator + (const complex &x,const complex &y){return complex (x.r+y.r,x.i+y.i);}complex operator - (const complex &x,const complex &y){return complex (x.r-y.r,x.i-y.i);}complex operator * (double k,const complex &y){return complex (k*y.r,k*y.i);}complex operator * (const complex &y,double k){return complex (k*y.r,k*y.i);}complex operator * (const complex &x,const complex &y){return complex (x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}complex operator / (const complex &x,double y){return complex (x.r/y,x.i/y);}complex operator / (const complex &x,const complex &y){return x*y.bar()/y.len2();}const double pi=acosl(-1.0 );char s[N],t[N];complex a[N],b[N],v[N];int rev[N],ans[N];int lens,lent,len;void DFT (complex c[],int inv=0 ) { for (int i=0 ;i<len;i++) v[rev[i]]=c[i]; for (int i=2 ;i<=len;i<<=1 ) { complex wn (cosl(2 *pi/i),sinl(2 *pi/i)) ; for (int j=0 ;j<len;j+=i) { complex w (1 ,0 ) ; for (int k=0 ;k<(i>>1 );k++) { complex x=v[j+k],y=v[j+k+(i>>1 )]*w; v[j+k]=x+y; v[j+k+(i>>1 )]=x-y; w=w*wn; } } } if (inv) { for (int i=0 ;i<len;i++) v[i]=v[i]/len; for (int i=1 ,j=len-1 ;i<j;i++,j--) swap(v[i],v[j]); } for (int i=0 ;i<len;i++) c[i]=v[i]; } void multiple () { scanf ("%s" ,s);scanf ("%s" ,t); lens=strlen (s);lent=strlen (t);len=1 ; while (len<=lens+lent) len<<=1 ; for (int i=0 ;i<len;i++) { a[i]=b[i]=complex (0 ,0 );ans[i]=0 ; } for (int i=0 ;i<lens;i++) a[i]=complex (s[lens-1 -i]-'0' ,0 ); for (int i=0 ;i<lent;i++) b[i]=complex (t[lent-1 -i]-'0' ,0 ); for (int i=0 ;i<len;i++) { rev[i]=0 ; for (int j=1 ,t=i;j<len;j<<=1 ,t>>=1 ) {rev[i]<<=1 ;rev[i]+=t&1 ;} } DFT(a);DFT(b); for (int i=0 ;i<len;i++) b[i]=a[i]*b[i]; DFT(b,1 ); for (int i=0 ;i<len;i++) { ans[i]+=round(b[i].r); ans[i+1 ]+=ans[i]/10 ; ans[i]%=10 ; } while (len>1 &&ans[len-1 ]==0 ) len-=1 ; for (int i=len-1 ;i>=0 ;i--) cout <<ans[i]; cout <<"\n" ; } int main () { int T;cin >>T; while (T--) multiple(); return 0 ; }
2.逆DFT
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 vector <int > computeRev (int n) { vector <int >rev(n); for (int i=0 ;i<n;i++) { rev[i]=0 ; int j,t; for (j=1 ,t=i;j<n;j<<=1 ,t>>=1 ) {rev[i]<<=1 ;rev[i]+=t&1 ;} } return rev; } vector <complex > inverseDFT (vector <complex >y,int n) { vector <int >rev=computeRev(n); vector <complex > c (n) ; for (int i=0 ;i<n;i++) c[rev[i]]=y[i]; for (int i=2 ;i<=n;i<<=1 ) { complex wn (cosl(2 *pi/i),sinl(2 *pi/i)) ; for (int j=0 ;j<n;j+=i) { complex w (1 ,0 ) ; for (int k=0 ;k<(i>>1 );k++) { complex x=c[j+k],y=c[j+k+(i>>1 )]*w; c[j+k]=x+y; c[j+k+(i>>1 )]=x-y; w=w*wn; } } } for (int i=0 ;i<n;i++) c[i]=c[i]/n; for (int i=1 ,j=n-1 ;i<j;i++,j--) swap(c[i],c[j]); return c; } int main () { int k;cin >>k;int n=pow (2 ,k); vector <complex > y (n) ; double a; for (int i=0 ;i<n;i++) { cin >>a;y[i]=complex (a,0 ); } vector <complex >c=inverseDFT(y,n); for (int i=0 ;i<n;i++) { if (abs (c[i].r-0 )<0.005 ) c[i].r=0 ; if (abs (c[i].i-0 )<0.005 ) c[i].i=0 ; printf ("%.2lf %.2lf\n" ,c[i].r,c[i].i); } return 0 ; }
3.啰嗦的解释
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
字符串
1.KMP
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 #include <iostream> #include <vector> using namespace std ; vector <int >prefix(string s){ int m=(int )s.length(); vector <int >pi(m); pi[0 ]=0 ; for (int i=1 ;i<m;i++) { int j=pi[i-1 ]; while (j>0 &&s[i]!=s[j]) j=pi[j-1 ]; if (s[i]==s[j]) j++; pi[i]=j; } return pi; } void kmp (string T,string P) { int n=(int )T.length(),m=(int )P.length(); vector <int >pi=prefix(P); int q=0 ; for (int i=0 ;i<n;i++){ while (q>0 &&P[q]!=T[i]) q=pi[q]; if (P[q]==T[i]) q+=1 ; if (q==m) { cout <<"Pattern occurs with shift " <<i-m; q=pi[q]; } } } void solve (string &s) { vector <int >pi=prefix(s),ans; int x=s.length(); while (x){ ans.push_back(x); x=pi[x-1 ]; } for_each(ans.rbegin(),ans.rend(),[](const int &x){cout <<x<<' ' ;}); cout <<endl ; } int main () { ios::sync_with_stdio(false ); cin .tie(0 ); int t;cin >>t; string s; while (t--){ cin >>s;solve(s); } return 0 ; } int main () { int T;cin >>T; while (T--){ string s;cin >>s; vector <int >pi=prefix(s); int n=(int )s.length(),cnt=0 ,q=0 ; int i=n-1 ; while (i>=0 ) { q=pi[i]; while (2 *q>i+1 ) q=pi[q-1 ]; if (q==0 ) {cnt+=1 ;i-=1 ;} else {cnt+=1 ;i-=q;} } cout <<cnt<<endl ; } return 0 ; }
2.状态机
由字符串S构建的字符串匹配自动机共有n+1个状态,其中n为字符串S的长度。第i个状态表示接收到的字符串的最后i−1个字符(长度为i−1的后缀)恰能匹配S的前i−1个字符,第n+1个状态表示已经匹配上字符串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 void compute_Trans (string &s) { int n=(int )s.length(); vector <int >pi=prefix(s); int **Trans=(int **)malloc (sizeof (int *)*n); for (int i=0 ;i<n;i++) Trans[i]=(int *)malloc (sizeof (int )*10 ); for (int j=0 ;j<10 ;j++){ if (s[0 ]=='a' +j) Trans[0 ][j]=1 ; else Trans[0 ][j]=0 ; } for (int i=1 ;i<n;i++){ for (int j=0 ;j<10 ;j++){ if (s[i]=='a' +j) {Trans[i][j]=i+1 ;continue ;} int q=pi[i-1 ]; while (q>0 &&s[q]!='a' +j) q=pi[q-1 ]; if (s[q]=='a' +j) Trans[i][j]=q+1 ; else Trans[i][j]=0 ; } } for (int i=0 ;i<n;i++){ for (int j=0 ;j<10 ;j++) printf ("%d " ,Trans[i][j]); printf ("\n" ); } }
3.字符串映射
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 #define N 100005 int n;char s[N],t[N];int main () { int T;cin >>T; while (T--){ scanf ("%d" ,&n); scanf ("%s" ,s);scanf ("%s" ,t); unordered_map <char ,char >m1,m2; unordered_map <char ,char >::iterator it1=m1.begin(),it2=m2.begin(); int flag=0 ; for (int i=0 ;i<n;i++) { it1=m1.find(s[i]);it2=m2.find(t[i]); if (it1==m1.end()&&it2==m2.end()) { m1.insert(make_pair (s[i],t[i])); m2.insert(make_pair (t[i],s[i])); } else if (it1!=m1.end()&&it2==m2.end()) {flag=1 ;break ;} else if (it1==m1.end()&&it2!=m2.end()) {flag=1 ;break ;} else { if (it1->second!=t[i]||it2->second!=s[i]) {flag=1 ;break ;} } } if (flag==0 ) printf ("Yes\n" ); else printf ("No\n" ); memset (s,'\0' ,sizeof (char )*n); memset (t,'\0' ,sizeof (char )*n); } return 0 ; }
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 typedef long long ll;#define N 100005 const int B=26 ,M=998244353 ;int n;void prepose () { for (int i=p[0 ]=1 ;i<N;i++) p[i]=(ll)p[i-1 ]*B%M; } int main () { ios::sync_with_stdio(false ); cin .tie(0 ); unordered_map <ll,pair <string ,int >>map ; unordered_map <ll,pair <string ,int >>::iterator it=map .begin(); cin >>n; string s; int maxsize=1 ; for (int i=0 ;i<n;i++){ cin >>s; int m=(int )s.size(); ll k=0 ; for (int j=0 ;j<m;j++) k=(k*B+s[j])%M; it=map .find(k); if (it==map .end()) map .insert(make_pair (k,make_pair (s,1 ))); else { if (s!=it->second.first) map .insert(make_pair (k,make_pair (s,1 ))); else { it->second.second+=1 ; if (it->second.second>maxsize) maxsize=it->second.second; } } } cout <<map .size()<<" " <<maxsize; return 0 ; }
5.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 44 45 46 47 48 typedef long long ll;#define N 100005 const int B=26 ,M=998244353 ;int n;ll Hash (string &s) { map <char ,int >m; map <char ,int >::iterator it=m.begin(); int len=(int )s.size(); ll k=0 ; for (int j=0 ;j<len;j++){ it=m.find(s[j]); if (it==m.end()) { m.emplace(s[j],m.size()+1 ); k=(k*B+m.size())%M; } else k=(k*B+it->second)%M; } return k; } int main () { ios::sync_with_stdio(false ); cin .tie(0 ); unordered_map <ll,int >whole_map; unordered_map <ll,int >::iterator it1=whole_map.begin(); cin >>n;int maxsize=1 ; string s; for (int i=0 ;i<n;i++){ cin >>s; ll k=Hash(s); it1=whole_map.find(k); if (it1==whole_map.end()) whole_map.insert(make_pair (k,1 )); else { it1->second+=1 ; if (it1->second>maxsize) maxsize=it1->second; } } cout <<whole_map.size()<<" " <<maxsize; return 0 ; }