首先声明!!!
1.内容可能有点多,涵盖大部分的基础算法,后续补充。
2.此为总结归纳,可能会有雷同,如有使用注明出处。
3.如有改进地方欢迎批评指正~@(。・o・)@~
一、基础算法
1.排序:
1.1 冒泡排序:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int g[N];
//冒泡排序
void bubble_sort()
{
for(int i=0;i<n;i++)
for(int j=0;j<n-i-1;j++)
if(g[j]>g[j+1])
swap(g[j],g[j+1]);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>g[i];
bubble_sort();
for(int i=0;i<n;i++) cout<<g[i]<<' ';
return 0;
}
1.2 选择排序
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int g[N];
//选择排序
void selection_sort()
{
for(int i=0;i<n-1;i++){
int min=i;
for(int j=i+1;j<n;j++){
if(g[j]<g[min])
min=j;
}
swap(g[min],g[i]);
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>g[i];
selection_sort();
for(int i=0;i<n;i++) cout<<g[i]<<' ';
return 0;
}
1.3 插入排序
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int g[N];
//插入排序
void insertion_sort()
{
for(int i=1;i<n;i++){
int res=g[i];
int j=i-1;
while(j>=0&&g[j]>res){
g[j+1]=g[j];
j--;
}
g[j+1]=res;
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>g[i];
insertion_sort();
for(int i=0;i<n;i++) cout<<g[i]<<' ';
return 0;
}
1.4 快速排序
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int q[N];
int n;
//快速排序
void quick_sort(int q[],int l,int r)
{
if(l>=r) return;
int x=q[(l+r)>>1],i=l-1,j=r+1;
while(i<j){
do i++; while(q[i]<x);
do j--; while(q[j]>x);
if(i<j) swap(q[i],q[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
1.5 归并排序
#include<iostream>
#include<algorithm>
using namespace std;
const int N =1e5+10;
int q[N],tmp[N];
int n;
//归并排序
void mergesort(int q[],int l,int r)
{
if(l>=r) return;
int mid=l+r>>1;
mergesort(q,l,mid),mergesort(q,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r){
if(q[i]<=q[j]) tmp[k++]=q[i++];
else tmp[k++]=q[j++];
}
while(i<=mid) tmp[k++]= q[i++];
while(j<=r) tmp[k++]=q[j++];
for (i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++ ) scanf("%d",&q[i]);
mergesort(q,0,n-1);
for (int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
2.二分
2.1 整数二分
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int q[N];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
while(m--){
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r){
int mid=l+r>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[l]!=x) printf("-1 -1\n");
else{
printf("%d ",l);
int l=0,r=n-1;
while(l<r){
int mid=l+r+1>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
}
return 0;
}
2.2 小数二分
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
double n;
cin>>n;
double l=-10000,r=10000;
while(r-l>1e-8){
double mid=(l+r)/2;
if(mid*mid*mid>=n) r=mid;
else l=mid;
}
printf("%.6lf",l);
return 0;
}
3.前缀和
3.1 线性前缀和
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int g[N],s[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>g[i],s[i]=s[i-1]+g[i];
while(m--){
int l,r;
cin>>l>>r;
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
3.2 二维前缀和
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1010;
int n,m,p;
int g[N][N],s[N][N];
int main()
{
cin>>n>>m>>p;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>g[i][j];
s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+g[i][j];
}
}
while(p--){
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
LL sum=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
cout<<sum<<endl;
}
return 0;
}
4.差分
4.1 线性差分
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i]-a[i-1];
}
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;i++){
a[i]=a[i-1]+b[i];
printf("%d ",a[i]);
}
return 0;
}
4.2 二维差分
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],b[N][N];
//差分函数
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
cin>>n>>m>>q;
//预处理出差分
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
insert(i,j,i,j,a[i][j]);
}
}
//查询时操作
while(q--){
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
//差分还原后输出
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
printf("%d ",b[i][j]);
}
printf("\n");
}
return 0;
}
5.双指针
5.1 头尾指针
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n,m,x;
int main()
{
cin>>n>>m>>x;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
int i,j=m-1;
for(i=0;i<n;i++){
while(j>=0&&a[i]+b[j]>x) j--;
if(a[i]+b[j]==x) break;
}
cout<<i<<' '<<j<<endl;
return 0;
}
5.2 头双指针
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<m;i++) cin>>b[i];
for(int i=0,j=0;i<m;i++){
if(a[j]==b[i]) j++;
if(j==n){
printf("Yes");
return 0;
}
}
printf("No");
return 0;
}
5.3 快慢指针(链表)
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode middleNode(ListNode head) {
ListNode p = head, q = head;
while(q != null && q.next != null){
q = q.next.next;
p = p.next;
}
return p;
}
}
6.贪心
6.1 区间选点
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct Range{
int l,r;
}range[N];
bool cmp(Range a,Range b)
{
if(a.r==b.r) return a.l<b.r;
return a.r<b.r;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>range[i].l>>range[i].r;
sort(range,range+n,cmp);
int sum=1,res=range[0].r;
for(int i=1;i<n;i++){
if(res<range[i].l){
res=range[i].r;
sum++;
}
}
cout<<sum<<endl;
return 0;
}
6.2 区间分组
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue <int,vector<int>,greater<int> > heap;
const int N=1e5+10;
struct Range{
int l,r;
}range[N];
bool cmp(Range a,Range b){
return a.l<b.l;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>range[i].l>>range[i].r;
sort(range,range+n,cmp);
for(int i=0;i<n;i++){
Range r=range[i];
if(heap.empty()||heap.top()>=r.l) heap.push(r.r);
else{
heap.pop();
heap.push(r.r);
}
}
cout<<heap.size()<<endl;
return 0;
}
6.3 哈夫曼树
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;
int main()
{
int n,p;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&p);
q.push(p);
}
int x,y,sum=0;
while(q.size()>1){
x=q.top();
q.pop();
y=q.top();
q.pop();
q.push(x+y);
sum+=x+y;
}
printf("%d",sum);
return 0;
}
6.4 选址问题
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n;
int g[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>g[i];
sort(g,g+n);
LL sum=0;
for(int i=0;i<n;i++) sum+=abs(g[i]-g[n/2]);
cout<<sum<<endl;
return 0;
}
6.5 推公式
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n;
LL ct[N];
struct P{
int x,y,z;
}g[N];
bool cmp(P a,P b){
return a.z<b.z;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
int a,b;
cin>>a>>b;
g[i].x=a,g[i].y=b,g[i].z=a+b;
}
sort(g+1,g+n+1,cmp);
for(int i=1;i<=n;i++) ct[i]=ct[i-1]+g[i].x;
LL sum=-2e17;
for(int i=1;i<=n;i++){
LL t=ct[i-1]-g[i].y;
sum=max(sum,t);
}
cout<<sum<<endl;
return 0;
}
7.位运算
7.1 朴素版
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int check(int n)
{
int sum=0;
while(n){
if(n&1) sum++;
n>>=1;
}
return sum;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
printf("%d ",check(a[i]));
}
return 0;
}
7.2 x&-x
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int x,s=0;
scanf("%d",&x);
for(int i=x;i;i-=i&-i) s++ ;
printf("%d ",s);
}
return 0;
}
二、数据结构
1.线性表
1.1 数组
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int g[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>g[i];
for(int i=0;i<n;i++) cout<<g[i]<<' ';
return 0;
}
1.2 哈希表
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+3,null=1e9+7;
int h[N];
int n;
int find(int x)
{
int k=(x%N+N)%N;
while(h[k]!=null&&h[k]!=x){
if(k==N) k=0;
k++;
}
return k;
}
int main()
{
scanf("%d",&n);
fill(h,h+N,null);
while(n--){
int x;
char op[2];
scanf("%s%d",&op,&x);
int k=find(x);
if(op[0]=='I') h[k]=x;
else{
if(h[k]==null) printf("No\n");
else printf("Yes\n");
}
}
return 0;
}
2.链表
说明:C++里有三种链表的应用方法
1.指针(最基本的链表)
2.结构体(链表的一个变种)
3.数组模拟(运行速度快,理解起来有点绕)
这里主要展示数组模拟~
2.1 单链表
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
//head 表示头结点的下标
//e[i] 表示节点i的值
//ne[i] 表示节点i的next指针是多少
//idx 存储当前已经用到了哪个点
int head,e[N],ne[N],idx;
//初始化
void init()
{
head=-1;
idx=0;
}
//在头节点后面插入节点
void add_to_head(int x)
{
e[idx]=x;
ne[idx]=head;
head=idx++;
}
//在k节点后面插入节点
void add(int k,int x)
{
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx++;
}
//删除节点
void remove(int k)
{
ne[k]=ne[ne[k]];
}
int main()
{
int m;
cin>>m;
init();
while(m--){
int k,x;
char op;
cin>>op;
if(op=='H'){
cin>>x;
add_to_head(x);
}
else if(op=='D'){
cin>>k;
if(!k) head=ne[head];
remove(k-1);
}
else{
cin>>k>>x;
add(k-1,x);
}
}
for(int i=head;i!=-1;i=ne[i])
cout<<e[i]<<' ';
return 0;
}
2.2 双链表
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int m;
int e[N],l[N],r[N],idx;
//初始化
void init()
{
r[0]=1,l[1]=0;
idx=2;
}
//在第k个点左右插入一个点
void add(int k,int x)
{
e[idx]=x;
r[idx]=r[k],l[idx]=k;
l[r[k]]=idx,r[k]=idx++;
}
//删除第k个点
void removes(int k)
{
r[l[k]]=r[k];
l[r[k]]=l[k];
}
int main()
{
init();
cin>>m;
while(m--){
string op;
int k,x;
cin>>op;
if(op=="L"){
cin>>x;
add(0,x);
}
else if(op=="R"){
cin>>x;
add(l[1],x);
}
else if(op=="D"){
cin>>k;
removes(k+1);
}
else if(op=="IL"){
cin>>k>>x;
add(l[k+1],x);
}
else{
cin>>k>>x;
add(k+1,x);
}
}
for(int i=r[0];i!=1;i=r[i])
cout<<e[i]<<' ';
return 0;
}
3.栈
3.1 STL容器
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
stack<int> st;
string s;
int n;
int main()
{
cin>>n;
while(n--){
cin>>s;
if(s=="push"){
int x;
cin>>x;
st.push(x);
}
else if(s=="empty"){
if(st.size()) printf("NO\n");
else printf("YES\n");
}
else if(s=="query")
printf("%d\n",st.top());
else st.pop();
}
return 0;
}
3.2 数组模拟
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int st[N],tt=-1;
string s;
int main()
{
cin>>n;
while(n--){
cin>>s;
if(s=="push"){
int x;
cin>>x;
st[++tt]=x;
}
else if(s=="empty"){
if(tt!=-1) printf("NO\n");
else printf("YES\n");
}
else if(s=="query")
printf("%d\n",st[tt]);
else tt--;
}
return 0;
}
4.队列
4.1 STL容器
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
queue<int> q;
string s;
int n;
int main()
{
cin>>n;
while(n--){
cin>>s;
if(s=="push"){
int x;
cin>>x;
q.push(x);
}
else if(s=="empty"){
if(q.size()) printf("NO\n");
else printf("YES\n");
}
else if(s=="query")
printf("%d\n",q.front());
else q.pop();
}
return 0;
}
4.2 数组模拟
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n;
int q[N],hh=0,tt=-1;
string s;
int main()
{
cin>>n;
while(n--){
cin>>s;
if(s=="push"){
int x;
cin>>x;
q[++tt]=x;
}
else if(s=="empty"){
if(tt<hh) printf("YES\n");
else printf("NO\n");
}
else if(s=="pop") hh++;
else printf("%d\n",q[hh]);
}
return 0;
}
5.树
5.1 并查集
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int fa[N];
int n,m;
int find(int x)
{
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) fa[i]=i;
while(m--){
int a,b;
char op[2];
scanf("%s%d%d",&op,&a,&b);
if(op[0]=='M') fa[find(a)]=find(b);
else{
if(find(a)==find(b)) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
5.2 Trie树
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
//下标是0的点,既是根结点,又是空节点
//idx是节点,son[p][u]为指向下一个节点的指针
int son[N][26],cnt[N],idx=0;
char str[N];
//插入节点
void insert(char str[])
{
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
cnt[p]++;
}
//查询节点
int query(char str[])
{
int p=0;
for(int i=0;str[i];i++){
int u=str[i]-'a';
if(!son[p][u]) return 0;
p=son[p][u];
}
return cnt[p];
}
int main()
{
int n;
scanf("%d",&n);
while(n--){
char op[2];
scanf("%s%s",&op,&str);
if(op[0]=='I') insert(str);
else printf("%d\n",query(str));
}
return 0;
}
6.图
6.1 邻接矩阵
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,INF=1e9+7;
int g[N][N];
int n,m;
int main()
{
cin>>n>>m;
fill(g[0],g[0]+N*N,INF);
while(m--){
int a,b,c;
cin>>a>>b>>c;
//有向图
g[a][b]=min(g[a][b],c);
//无向图
g[a][b]=g[b][a]=min(g[a][b],c);
}
return 0;
}
6.2 邻接表
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10,INF=1e9+7;
int h[N],e[N],ne[N],w[N],idx;
int n,m;
//添加节点
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int main()
{
scanf("%d%d",&n,&m);
fill(h,h+N,-1);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
//有向图
add(a,b,c);
//无向图
add(a,b,c),add(b,a,c);
}
return 0;
}
7.C++ STL容器及函数(只列出较为常用的)
7.1 常用函数*
#include<iostream>
#include<algorithm> //头文件
using namespace std;
int main()
{
//1.max()、min()、abs()函数
int a = 3, b = 4;
//求最大值
int Max = max(a,b);
//求最小值
int Min = min(a,b);
//求绝对值
int Abs = abs(-3);
cout << Max << Min << Abs;
//2.交换函数:swap()
int a = 3, b = 4;
swap(a,b);
//3.排序函数:sort()
int a[5] = {55,44,33,22,11};
sort(a,a+5);
for(int i = 0; i < 5; i++)
cout << a[i] << ' ';
//4.填充函数:fill() (很多人都用memset,但是我就喜欢fill)
int a[5] = {11,33,22};
fill(a+3,a+5,9999);
for(int i = 0; i < 5; i++)
cout << a[i] << ' ';
//5.翻转函数:reverse()
int a[5] = {11,22,33,44,55};
reverse(a,a+5);
for(int i = 0; i < 5; i++)
cout << a[i] << ' ';
return 0;
}
7.2 vector(动态数组)
#include<iostream>
#include<algorithm>
#include<vector> //头文件
using namespace std;
//初始化
//方式一:初始化一维可变长数组
vector<int> c; //定义了一个名为num的存int数据的一维数组
vector<double> c;//定义了一个名为num的存double数据的一维数组
vector<node> c;//node是结构体类型
//方式二:初始化二维可变长数组
vector<int> c[5];//定义可变长二维数组
//注意:行是不可变的(只有5行),而列可变可以在指定行添加元素
//第一维固定长度为5,第二维长度可以改变
//方式三:初始化二维均可变长数组
vector<vectot<int> > c;//定义一个行和列均可变的二维数组
int main()
{
//访问
//方式一:单个访问,假设num数组中已经有了5个元素
cout<<c[4]<<"\n"; //输出第五个数据
//一二维可变数组和普通数组的访问方法一样
//方式二:遍历
for(int i=0;i<c.size();i++)
cout<<c[i]<<" ";
//方式三:智能指针
for(auto i : c)
cout<<i<<" ";
return 0;
}
---
相关方法函数如下:c指定为数组名称
代码 含义
c.front() 返回第一个数据
c.back() 返回最后一个数据
c.push_back(element) 在尾部加一个数据 O(1)
c.pop_back() 删除最后一个数据 O(1)
c.size() 返回实际数据个数(unsigned类型) O(1)
c.clear() 清除元素个数 O(N),N为元素个数
c.resize(n,v) 改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0
c.insert(it,x)
向任意迭代器it插入一个元素x O(N),
例:c.insert(c.begin()+2,-1) 将-1插入c[2]的位置
c.erase(first,last) 删除[first,last)的所有元素
c.begin() 返回首元素的迭代器(通俗来说就是地址)
c.end() 返回最后一个元素后一个位置的迭代器(地址)
c.empty() 判断是否为空,为空返回真,反之返回假
注意: end()返回的是最后一个元素的后一个位置的地址,不是最后一个元素的
---
7.3 stack(栈)
#include<iostream>
#include<algorithm>
#include<stack> //头文件
using namespace std;
//初始化
stack<int> st;
stack<string> st;
stack<node> st;//node是结构体类型
int main()
{
//访问函数
push() 压栈,增加元素 O(1)
pop() 移除栈顶元素 O(1)
top() 取得栈顶元素(但不删除)O(1)
empty() 检测栈内是否为空,空为真 O(1)
size() 返回stack内元素的个数 O(1)
return 0;
}
7.4 queue(队列)
#include<iostream>
#include<algorithm>
#include<queue> //头文件
using namespace std;
//普通队列初始化
queue<int> q;
queue<string> q;
queue<node> q;//node是结构体类型
//优先队列初始化定义
priority_queue<int> q;
//优先队列设置优先级
priority_queue<int, vector<int>, greater<int> > q;
//最后两个>之间要有空格,旧版c++编译器会把">>"当成右移符!!!
int main()
{
//普通队列访问函数
front() 返回队首元素 O(1)
back() 返回队尾元素 O(1)
push() 尾部添加一个元素副本 进队O(1)
pop() 删除第一个元素 出队 O(1)
size() 返回队列中元素个数,返回值类型unsigned int O(1)
empty() 判断是否为空,队列为空,返回true O(1)
//优先队列访问函数
top() 访问队首元素
push() 入队
pop() 堆顶(队首)元素出队
size() 队列元素个数
empty() 是否为空
注意没有clear()! 不提供该方法
优先队列只能通过top()访问队首元素(优先级最高的元素)
//注:栈的取第一个元素为top();而普通队列的为front();优先队列为top();
return 0;
}
7.5 pair
#include<iostream>
#include<algorithm>
using namespace std;
//初始化
pair<string,int>p("wangyaqi",1);//带初始值的
pair<string,int>p;//不带初始值的
int main()
{
//访问函数
pair<int,int>p[20];
for(int i=0;i<20;i++){
//和结构体类似,first代表第一个元素,second代表第二个元素
cout<<p[i].first<<" "<<p[i].second;
}
return 0;
}
7.6 string字符串
#include<iostream>
#include<algorithm>
#include<string> //头文件(可写可不写,iostream里自带)
using namespace std;
//定义
string s;
int main()
{
//使用操作
for(int i=0;i<s.size();i++)
cout<<s[i]<<" ";
//拼接
string s1;
string s2;
s1 = "123";
s2 = "456";
string s = s1 + s2;
cout<<s; //123456
return 0;
}
三、搜索和图论
1.DFS深度优先搜索
1.1 基础深搜
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10;
int path[N];
bool st[N];
int n;
void dfs(int u)
{
if(u==n){
for(int i=0;i<n;i++) printf("%d ",path[i]);
printf("\n");
return;
}
for(int i=1;i<=n;i++){
if(!st[i]){
path[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20;
bool col[N],dg[N],udg[N];
char g[N][N];
int n;
void dfs(int u)
{
if(u==n){
for(int i=0;i<n;i++) puts(g[i]);
puts("");
return;
}
for(int i=0;i<n;i++){
if(!col[i]&&!dg[u+i]&&!udg[n-u+i]){
g[u][i]='Q';
col[i]=dg[u+i]=udg[n-u+i]=true;
dfs(u+1);
col[i]=dg[u+i]=udg[n-u+i]=false;
g[u][i]='.';
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0);
return 0;
}
1.2 图的深搜
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int h[N],e[2*N],ne[2*N],idx;
bool st[N];
int n;
int ans=N;
//邻接表存储图
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//以u为根的子树中点的数量
int dfs(int u)
{
st[u]=true;
int sum=1,res=0;
//sum存当前子树节点节点总个数
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(!st[j]){
int s=dfs(j);
//搜索子树节点个数并累加到s中
res=max(res,s);
//更新res值
sum+=s;
//累加节点u的子树节点
}
}
//res存当前子树节点最大值
res=max(res,n-sum);
//ans存所有节点的res最小值(重心)
ans=min(ans,res);
//返回子树节点总数
return sum;
}
int main()
{
fill(h,h+N,-1);
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
dfs(1);
cout<<ans<<endl;
return 0;
}
2.BFS广度优先搜索
2.1 基础搜索
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int,int> PII;
const int N=110;
int g[N][N],d[N][N];
PII q[N*N];
int n,m;
int s[2][4]={
0,0,1,-1,
1,-1,0,0
};
int bfs()
{
int hh=0,tt=0;
q[0]={0,0};
fill(d[0],d[0]+N*N,-1);
d[0][0]=0;
while(hh<=tt){
PII t=q[hh++];
for(int i=0;i<4;i++){
int x=t.first+s[0][i];
int y=t.second+s[1][i];
if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]==-1){
d[x][y]=d[t.first][t.second]+1;
q[++tt]={x,y};
}
}
}
return d[n-1][m-1];
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
cout<<bfs()<<endl;
return 0;
}
2.2 图的广搜
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx;
int q[N],d[N];
int n,m;
//邻接表存储图
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int bfs()
{
int hh=0,tt=0;
fill(d,d+N,-1);
q[0]=1,d[1]=0;
//数组q模拟队列
while(hh<=tt){
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]==-1){
d[j]=d[t]+1;
q[++tt]=j;
}
}
}
//d[n]记录路径长度+判断
return d[n];
}
int main()
{
cin>>n>>m;
//fill填充h数组
fill(h,h+N,-1);
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
cout<<bfs()<<endl;
return 0;
}
3.最短路径算法
3.1 朴素Dijkstra最短路(不存在负权边)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,INF=1e9+7;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
int dijkstra()
{
fill(dist,dist+N,INF);
dist[1]=0;
for(int i=0;i<n;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
st[t]=true;
}
return dist[n];
}
int main()
{
cin>>n>>m;
fill(g[0],g[0]+N*N,INF);
while(m--){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
int t=dijkstra();
if(t==INF) printf("-1");
else printf("%d",dist[n]);
return 0;
}
3.2 堆优化Dijkstra最短路
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e6+10,INF=1e9+7;
typedef pair<int,int> PII;
int h[N],w[N],e[N],ne[N],idx;
int dist[N],n,m;
bool st[N];
void add(int a,int b,int c)
{
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int dijkstra()
{
fill(dist,dist+N,INF);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII> > heap;
heap.push({0,1});
while(heap.size()){
PII t=heap.top();
heap.pop();
int ver=t.second,distance=t.first;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>distance+w[i]){
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
if(dist[n]==INF) return -1;
return dist[n];
}
int main()
{
scanf("%d%d",&n,&m);
fill(h,h+N,-1);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
printf("%d",dijkstra());
return 0;
}
3.3 bellman-ford最短路(有边数限制)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510,M=10010,INF=1e9+7;
int dist[N],backup[N];
int n,m,k;
struct Edge{
int a,b,w;
}edges[M];
void bellman_ford()
{
fill(dist,dist+N,INF);
dist[1]=0;
for(int i=0;i<k;i++){
memcpy(backup,dist,sizeof dist);
for(int j=0;j<m;j++){
int a=edges[j].a,b=edges[j].b,w=edges[j].w;
dist[b]=min(dist[b],backup[a]+w);
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i]={a,b,w};
}
bellman_ford();
if(dist[n]>INF/2) printf("impossible");
else printf("%d",dist[n]);
return 0;
}
3.4 spfa最短路(存在负权边)
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10,INF=1e9+7;
int h[N],e[N],ne[N],w[N],idx;
int dist[N],n,m;
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void spfa()
{
fill(dist,dist+N,INF);
dist[1]=0;
queue<int> q;
q.push(1);
st[1]=true;
while(q.size()){
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
fill(h,h+N,-1);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa();
if(dist[n]==INF) printf("impossible");
else printf("%d",dist[n]);
return 0;
}
3.5 Floyd最短路(多源)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,INF=1e9+7;
int g[N][N];
int n,m,k;
void floyd()
{
for(int p=1;p<=n;p++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=min(g[i][j],g[i][p]+g[p][j]);
}
int main()
{
cin>>n>>m>>k;
fill(g[0],g[0]+N*N,INF);
for(int i=1;i<=n;i++) g[i][i]=0;
while(m--){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
floyd();
while(k--){
int a,b;
cin>>a>>b;
if(g[a][b]>=1e6) printf("impossible\n");
else printf("%d\n",g[a][b]);
}
return 0;
}
4.最小生成树
4.1 Prim算法
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,INF=1e9+7;
int g[N][N],dist[N];
bool st[N];
int n,m;
int prim()
{
//初始化dist数组
fill(dist,dist+N,INF);
int res=0;
for(int i=0;i<n;i++){
//找到距离集合最小权的边并赋予t
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
//把t加到集合里
st[t]=true;
//i!=0,把边加到res里
if(i){
//如果最小值为INF,则无边到集合中
if(dist[t]==INF) return INF;
res+=dist[t];
}
//更新节点到集合的最短距离
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],g[t][j]);
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
fill(g[0],g[0]+N*N,INF);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a][b]=g[b][a]=min(g[a][b],c);
}
int t=prim();
if(t==INF) printf("impossible");
else printf("%d",t);
return 0;
}
4.2 Kruskal算法
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,m;
int p[N];
struct Edge{
int a,b,w;
}edges[N];
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i]={a,b,w};
}
sort(edges,edges+m,cmp);
for(int i=1;i<=n;i++) p[i]=i;
int sum=0,ans=0;
for(int i=0;i<m;i++){
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
a=find(a),b=find(b);
if(a!=b){
p[a]=b;
sum+=w;
ans++;
}
}
if(ans<n-1) printf("impossible");
else printf("%d",sum);
return 0;
}
5.二分图
5.1 染色法判定
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10,M=2*N;
int h[N],e[M],ne[M],idx;
int color[N];
int n,m;
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool dfs(int u,int c)
{
color[u]=c;
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(!color[j]&&!dfs(j,3-c)) return false;
if(color[j]==c) return false;
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
fill(h,h+N,-1);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
bool flag=true;
for(int i=1;i<=n;i++){
if(!color[i]&&!dfs(i,1)){
flag=false;
break;
}
}
if(flag) printf("Yes");
else printf("No");
return 0;
}
5.2 匈牙利算法匹配
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,M=1e5+10;
int h[N],e[M],ne[M],idx;
int match[N];
int n1,n2,m;
bool st[N];
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
bool find(int x)
{
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
if(!st[j]){
st[j]=true;
if(!match[j]||find(match[j])){
match[j]=x;
return true;
}
}
}
return false;
}
int main()
{
scanf("%d%d%d",&n1,&n2,&m);
fill(h,h+N,-1);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
int res=0;
for(int i=1;i<=n1;i++){
fill(st,st+N,false);
if(find(i)) res++;
}
printf("%d",res);
return 0;
}
四、数论
1.质数(素数)
1.1 试除法求质数
#include<iostream>
#include<algorithm>
using namespace std;
bool prime(int x)
{
if(x<2) return false;
for(int i=2;i<=x/i;i++)
if(x%i==0)
return false;
return true;
}
int main()
{
int n;
cin>>n;
while(n--){
int x;
cin>>x;
if(prime(x)) printf("Yes\n");
else printf("No\n");
}
return 0;
}
1.2 分解质因数
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
cin>>n;
while(n--){
int x;
cin>>x;
for(int i=2;i<=x/i;i++){
if(x%i==0){
int ans=0;
while(x%i==0){
x/=i;
ans++;
}
printf("%d %d\n",i,ans);
}
}
if(x>1) printf("%d %d\n",x,1);
printf("\n");
}
return 0;
}
1.3 筛质数
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;
bool st[N];
int n,cnt;
void get_prime(int n)
{
for(int i=2;i<=n;i++){
if(!st[i]) cnt++;
for(int j=i+i;j<=n;j+=i){
st[j]=true;
}
}
}
int main()
{
scanf("%d",&n);
get_prime(n);
printf("%d",cnt);
return 0;
}
2. 约数(因数)
2.1 试除法求约数
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> primes;
int n;
vector<int> prime(int x)
{
vector<int> p;
for(int i=1;i<=x/i;i++){
if(x%i==0){
p.push_back(i);
if(x/i!=i) p.push_back(x/i);
}
}
sort(p.begin(),p.end());
return p;
}
int main()
{
cin>>n;
while(n--){
int x;
cin>>x;
primes=prime(x);
for(int i=0;i<primes.size();i++) printf("%d ",primes[i]);
printf("\n");
}
return 0;
}
2.2 约数个数
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
int main()
{
int n;
cin>>n;
unordered_map<int,int> primes;
while(n--){
int x;
cin>>x;
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
primes[i]++;
}
}
if(x>1) primes[x]++;
}
long long ans=1;
for(auto t:primes) ans=ans*(t.second+1)%mod;
cout<<ans<<endl;
return 0;
}
2.3 约数之和
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
int main()
{
int n;
cin>>n;
unordered_map<int,int> primes;
while(n--){
int x;
cin>>x;
//试除法判断约数并累加
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
primes[i]++;
}
}
if(x>1) primes[x]++;
}
long long ans=1;
for(auto t:primes){
//t遍历哈希primes
int p=t.first,s=t.second;
//p为n的第k个约数,s为第k个约数的个数
long long sum=1;
//求(1+p1^1+p1^2+...+p1^s);
while(s--) sum=(sum*p+1)%mod;
//求(1+p2^1+...+p1^s)*(1+p2^1+...+p2^s)*...*(1+pk^1+...+pk^s);
ans=ans*sum%mod;
}
cout<<ans<<endl;
return 0;
}
3.大整数运算
3.1 加法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> A,B,C;
string a,b;
vector<int> add(vector<int> &A,vector<int> &B)
{
if(A.size()<B.size()) return add(B,A);
vector<int> C;
int t=0;
for(int i=0;i<A.size()||i<B.size();i++){
if(i<A.size()) t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(1);
return C;
}
int main()
{
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
C=add(A,B);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
3.2 减法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> A,B,C;
string a,b;
bool cmp(vector<int> &A,vector<int> &B)
{
if(A.size()!=B.size()) return A.size()>B.size();
for(int i=A.size()-1;i>=0;i--){
if(A[i]!=B[i])
return A[i]>B[i];
}
return true;
}
vector<int> sub(vector<int> &A,vector<int> &B)
{
vector<int> c;
for(int i=0,t=0;i<A.size();i++){
t=A[i]-t;
if(i<B.size()) t-=B[i];
c.push_back((t+10)%10);
t<0?t=1:t=0;
}
while(c.size()>1&&c.back()==0) c.pop_back();
return c;
}
int main()
{
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0')
if(cmp(A,B)){
C=sub(A,B);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
}
else{
C=sub(B,A);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
}
return 0;
}
3.3 乘法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> A,C;
string a;
int b;
vector<int> mul(vector<int> &A,int b)
{
vector<int> c;
int t=0;
for(int i=0;i<A.size()||t;i++){
if(i<A.size()) t+=A[i]*b;
c.push_back(t%10);
t/=10;
}
return c;
}
int main()
{
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
C=mul(A,b);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
3.4 除法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
string a;
vector<int> A;
int B,r;
vector<int> div(vector<int> &A,int b)
{
vector<int> C;
for(int i=A.size()-1;i>=0;i--){
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
cin>>a>>B;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
vector<int> C=div(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
cout<<endl<<r<<endl;
return 0;
}
4.最大公约数(最小公倍数)
4.1 欧几里得算法(辗转相除法)
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int main()
{
int n;
cin>>n;
while(n--){
int a,b;
cin>>a>>b;
//最大公约数
printf("%d\n",gcd(a,b));
//最小公倍数
printf("%d\n",a*gcd(a,b)/b);
}
return 0;
}
4.2 扩展欧几里得算法
#include<iostream>
#include<algorithm>
using namespace std;
// 公式ax+by=gcd(ai,bi)
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
int main()
{
int n;
scanf("%d",&n);
while(n--){
int a,b,x,y;
scanf("%d%d",&a,&b);
exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}
5.快速幂
5.1 基础快速幂
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL k(LL a,LL b,LL mod)
{
LL sum=1;
while(b){
if(b&1) sum=sum*a%mod;
a=a*a%mod;
b>>=1;
}
return sum%mod;
}
int main()
{
int n;
scanf("%d",&n);
while(n--){
LL a,b,p;
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld\n",k(a,b,p));
}
return 0;
}
5.2 快速幂求逆元
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL k(LL a,LL b,LL mod)
{
LL sum=1;
while(b){
if(b&1) sum=sum*a%mod;
a=a*a%mod;
b>>=1;
}
return sum%mod;
}
int main()
{
int n;
scanf("%d",&n);
while(n--){
LL a,p,t;
scanf("%lld%lld",&a,&p);
if(a%p) printf("%lld\n",k(a,p-2,p));
else printf("impossible\n");
}
return 0;
}
5.3 矩阵快速幂
#include<iostream>
#include<algorithm>
using namespace std;
//定义矩阵结构体
struct Matrix
{
int a[3][3];
Matrix() memset(a,0,sizeof(a));
//矩阵乘法
Matrix operator*(const Matrix &b) const
{
Matrix res;
for(int i=1; i<=2; i++)
for(int j=1; j<=2; j++)
for(int k=1; k<=2; k++)
res.a[i][j]=res.a[i][j]+a[i][k]*b.a[k][j];
return res;
}
};
Matrix base,ans;
//初始化base,ans
void init()
{
//矩阵
//[0,1]
//[1,1]
base.a[1][1]=0;//这个11位置一定要初始化
base.a[2][2]=base.a[1][2]=base.a[2][1]=1;
//初始斐波那契数列f1=1,f2=1
ans.a[1][1]=1;
ans.a[1][2]=1;
}
//快速幂
void qpow(int n)
{
//传入n次幂
while(n){
if(n&1)ans=ans*base;//n为奇数
base=base*base;
n>>=1;// n/=2
}
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
init();//每次初始化矩阵
qpow(n-2);//快速幂优化
cout<<ans.a[1][2]<<endl;
}
return 0;
}
6.组合数
6.1 普通递推
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010,mod=1e9+7;
int n;
int c[N][N];
int main()
{
scanf("%d",&n);
for(int i=1;i<N;i++){
c[i][1]=i;
c[i][i]=1;
}
for(int i=2;i<N;i++){
for(int j=2;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",c[a][b]);
}
return 0;
}
6.2 C(a,b) = a!/(b-a)!*b!(预处理)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10,mod=1e9+7;
int n;
LL f[N],inf[N];
LL q(LL a,LL k,LL p)
{
LL sum=1;
while(k){
if(k&1) sum=(LL)sum*a%p;
a=(LL)a*a%p;
k>>=1;
}
return sum;
}
int main()
{
scanf("%d",&n);
f[0]=inf[0]=1;
for(int i=1;i<N;i++){
f[i]=(LL)f[i-1]*i%mod;
inf[i]=(LL)inf[i-1]*q(i,mod-2,mod)%mod;
}
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",(LL)f[a]*inf[a-b]%mod*inf[b]%mod);
}
return 0;
}
6.3 C(a,b) = C(a%p,b%p)*C(a/p,b/p)(lucas定理)
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int p;
//快速幂求逆元
int q(LL a,LL k,LL p)
{
int res=1;
while(k){
if(k&1) res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
//求组合数
int c(LL a,LL b)
{
int sum=1;
for(int i=1,j=a;i<=b;i++,j--){
sum=(LL)sum*j%p;
sum=(LL)sum*q(i,p-2,p)%p;
}
return sum;
}
//卢卡斯定理
int lucas(LL a,LL b)
{
if(a<p&&b<p) return c(a,b);
return (LL)c(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main()
{
int n;
cin>>n;
while(n--){
LL a,b;
cin>>a>>b>>p;
cout<<lucas(a,b)<<endl;
}
return 0;
}
6.4 不取模运算C(a,b)
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=5010;
int primes[N],cnt;
int sum[N];
bool st[N];
//线性筛法
void get_primes(int n)
{
for(int i=2;i<=n;i++){
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
//求a!里p因子的个数
int get(int n,int p)
{
int res=0;
while(n){
res+=n/p;
n/=p;
}
return res;
}
//高精度乘法
vector<int> mul(vector<int> a,int b)
{
vector<int> c;
int t=0;
for(int i=0;i<a.size();i++){
t+=a[i]*b;
c.push_back(t%10);
t/=10;
}
while(t){
c.push_back(t%10);
t/=10;
}
return c;
}
int main()
{
int a,b;
cin>>a>>b;
get_primes(a);
for(int i=0;i<cnt;i++){
int p=primes[i];
sum[i]=get(a,p)-get(b,p)-get(a-b,p);
}
vector<int> res;
res.push_back(1);
for(int i=0;i<cnt;i++){
for(int j=0;j<sum[i];j++){
res=mul(res,primes[i]);
for(int i=res.size()-1;i>=0;i--)
printf("%d",res[i]);
return 0;
}
7.博弈论
7.1 Nim博弈
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
int sum=0;
while(n--){
int x;
scanf("%d",&x);
sum^=x;
}
if(sum) printf("Yes");
else printf("No");
return 0;
}
7.2 集合-Nim博弈(sg函数)
#include<iostream>
#include<algorithm>
#include<unordered_set>
using namespace std;
const int N=110,M=10010;
int n,k;
int s[N],f[M];
int sg(int x)
{
if(f[x]!=-1) return f[x];
unordered_set<int> S;
for(int i=0;i<k;i++)
if(s[i]<=x)
S.insert(sg(x-s[i]));
for(int i=0;;i++)
if(!S.count(i))
return f[x]=i;
}
int main()
{
cin>>k;
for(int i=0;i<k;i++) cin>>s[i];
cin>>n;
fill(f,f+M,-1);
int res=0;
while(n--){
int a;
cin>>a;
res^=sg(a);
}
if(res) printf("Yes");
else printf("No");
return 0;
}
五、动态规划
1.背包问题
1.1 01背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,V;
int dp[N],w[N],v[N];
int main()
{
cin>>n>>V;
for(int i=0;i<n;i++) cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
for(int j=V;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
cout<<dp[V]<<endl;
return 0;
}
1.2 完全背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int v[N],w[N],dp[N];
int n,m;
int main()
{
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++)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
cout<<dp[m]<<endl;
return 0;
}
1.3 多重背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110;
int v[N],w[N],s[N];
int dp[N][N];
int n,m;
int main()
{
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=1;j<=m;j++)
for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
cout<<dp[n][m]<<endl;
return 0;
}
1.4 分组背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110;
int v[N][N],w[N][N],s[N];
int dp[N];
int n,m;
int main()
{
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)
dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
cout<<dp[m]<<endl;
return 0;
}
2.线性dp
2.1 数字三角形
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n;
int g[N][N];
int dp[N][N];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<=i;j++)
cin>>g[i][j];
for(int i=n-1;i>=0;i--)
for(int j=0;j<=i;j++)
dp[i][j]=max(dp[i+1][j+1],dp[i+1][j])+g[i][j];
cout<<dp[0][0]<<endl;
return 0;
}
2.2 最长上升子序列
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n;
int g[N],dp[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>g[i];
fill(dp,dp+n,1);
int res=1;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(g[i]>g[j])
dp[i]=max(dp[i],dp[j]+1);
}
res=max(dp[i],res);
}
cout<<res<<endl;
return 0;
}
2.3 最长公共子序列
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int m,n;
int dp[N][N];
char a[N],b[N];
int main()
{
scanf("%d%d",&n,&m);
scanf("%s%s",a+1,b+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j])
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
printf("%d",dp[n][m]);
return 0;
}
2.4 最短编辑距离
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int m,n;
int dp[N][N];
char a[N],b[N];
int main()
{
scanf("%d%s",&n,a+1);
scanf("%d%s",&m,b+1);
//一定要考虑边界问题!!!
for (int i=0;i<=m;i++) dp[0][i]=i;
for (int i=0;i<=n;i++) dp[i][0]=i;
//相等即直接转换状态,不相等取所有状态的最小值再+1
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min(min(dp[i-1][j],dp[i-1][j-1]),dp[i][j-1])+1;
}
}
printf("%d",dp[n][m]);
return 0;
}
3.进阶dp
3.1 区间dp
#include<iostream>
#include<algorithm>
using namespace std;
const int N=310;
int n;
int s[N];
int dp[N][N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
int a;
scanf("%d",&a);
s[i]=s[i-1]+a;
}
for(int len=1;len<n;len++){
for(int i=1;i+len<=n;i++){
int l=i,r=i+len;
dp[l][r]=1e9+7;
for(int k=l;k<r;k++){
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+s[r]-s[l-1]);
}
}
}
cout<<dp[1][n]<<endl;
return 0;
}
3.2状态压缩dp
蒙德里安的梦想
#include<iostream>
#include<algorithm>
using namespace std;
const int N=12,M=1<<N;
long long dp[N][M];
int n,m;
bool st[M];
int main()
{
//预处理
while(cin>>n>>m,n||m){
fill(dp[0],dp[0]+N*M,0);
for(int i=0;i<1<<n;i++){
st[i]=true;
int cnt=0;
for(int j=0;j<n;j++){
if(i>>j&1){
if(cnt&1) st[i]=false;
cnt=0;
}
else cnt++;
}
if(cnt&1) st[i]=false;
}
dp[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<1<<n;j++)
for(int k=0;k<1<<n;k++)
if(!(j&k)&&st[j|k])
dp[i][j]+=dp[i-1][k];
cout<<dp[m][0]<<endl;
}
return 0;
}
状态压缩dp求哈密顿路径
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20,M=1<<N;
int n;
int w[N][N];
int dp[M][N];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>w[i][j];
fill(dp[0],dp[0]+M*N,0x3f3f3f);
dp[1][0]=0;
for(int i=0;i<1<<n;i++)
for(int j=0;j<n;j++)
if(i>>j&1)
for(int k=0;k<n;k++)
if((i-(1<<j))>>k&1)
dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+w[k][j]);
cout<<dp[(1<<n)-1][n-1]<<endl;
return 0;
}
3.3 树形dp
#include<iostream>
#include<algorithm>
using namespace std;
const int N=6010;
int n;
int happy[N];
int h[N],e[N],ne[N],idx;
int dp[N][2];
bool father[N];
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u)
{
dp[u][1]=happy[u];
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
dfs(j);
dp[u][0]+=max(dp[j][0],dp[j][1]);
dp[u][1]+=dp[j][0];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&happy[i]);
fill(h,h+N,-1);
for(int i=0;i<n-1;i++){
int a,b;
scanf("%d%d",&a,&b);
father[a]=true;
add(b,a);
}
int root=1;
while(father[root]) root++;
dfs(root);
printf("%d",max(dp[root][0],dp[root][1]));
return 0;
}
4.记忆化搜索
4.1 斐波那契(记录值)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int dp[N];
int n;
//斐波那契递归写法
int F(int n)
{
if(n==0||n==1) return 1;
if(dp[n]!=-1) return dp[N];
else{
dp[n]=F(n-1)+F(n-2);
return dp[n];
}
}
int main()
{
cin>>n;
fill(dp,dp+N,-1);
F(n);
return 0;
}
4.2 滑雪
#include<iostream>
#include<algorithm>
using namespace std;
const int N=310;
int n,m;
int g[N][N];
int f[N][N];
int s[2][4]={
1,-1,0,0,
0,0,1,-1
};
int dp(int x,int y)
{
if(f[x][y]!=0) return f[x][y];
f[x][y]=1;
for(int i=0;i<4;i++){
int a=x+s[0][i],b=y+s[1][i];
if(a>=1&&a<=n&&b>=1&&b<=m&&g[a][b]<g[x][y])
f[x][y]=max(f[x][y],dp(a,b)+1);
}
return f[x][y];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
int sum=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
sum=max(sum,dp(i,j));
cout<<sum<<endl;
return 0;
}