北邮机试模板

ps:整理收藏自网络,侵删

目录:

基本操作 1

C++ 常用头文件和宏定义 1

输入输出 2

二分 3

排序 3

图论 4

基本数据结构 4

最短路径 5

最小生成树 8

数据结构 9

并查集 9

二叉树的各类题型 10

Huffman树 13

线段树 14

搜索法 16

连通增量 16

DFS 16

BFS 18

排列组合 18

常用自带库与函数 20

C语言字符串函数 20

其他 20

algorithm 20

string 22

priority_queue 25

set 25

vector 27

数学问题 27

表达式求值 27

进制转换 30

排列组合 30

快速幂 31

数论 32

动态规划 33

背包问题 33

线性DP 34

区间DP 36

分治递归 37

归并排序求逆序对 37

台阶问题 38

传球游戏 38

 

基本操作

C++ 常用头文件和宏定义

  • 一了百了

#include <bits/stdc++.h>

  • 大军来袭

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>

  • 宏定义

#define FF(a,b) for(int a=0;a<b;a++)
#define F(a,b) for(int a=1;a<=b;a++)
#define LEN 200
#define INF ((1<<30)-1)
#define bug(x) cout<<#x<<"="<<x<<endl;
// 把INF定义为(INF+INF)还能保证不溢出的一个数
/*
INF+INF=2147483646
0x7FFFFFFF=2147483647
*/
using namespace std;
typedef long long ll;
const double pi=acos(-1);

输入输出

  • double

输入:%lf

输出:%f

  • scanf

返回值解析

返回正确读入的参数个数。

遇到文件结尾,返回EOF,也就是-1

安全的获取一行:fgets (buf, MAX, stdin)

  • printf

补前导0:%02d:总宽度为2,不足用0补

设置精度:%.2f保留两位小数

  • 输入流输出流

关同步

std::ios::sync_with_stdio(false)

获取一行

cin.get(name,SIZE);         //回车保留在输入队列中
cin.getline(name,SIZE);     //回车被清除

设置输出精度

cout<<setprecision(2)<<a;   //输出: 123.45

设置前导零:

cout<<setfill('0')<<setw(4)<<value<<endl;

二分

原生实现lower_bound和upper_bound

https://www.luogu.org/problemnew/show/P3366

  • lower_bound

    int l=0;    //初始化 l ,为第一个合法地址
    int r=10;    //初始化 r , 地址的结束地址
    int mid;
    while(l<r) {
        mid=(l+r)/2;
        if(arr[mid]>=obj){
            r=mid;
        }else{
            l=mid+1;
        }
    }

  • upper_bound

int l=0;    //初始化 l ,为第一个合法地址
    int r=10;    //初始化 r , 地址的结束地址
    int mid;
    while(l<r) {
        mid=(l+r)/2;
        if(arr[mid]>obj){  //没有=符号是与上文算法唯一的区别
            r=mid;
        }else{
            l=mid+1;
        }
    }

排序

结构体排序

  • 结构体内重载小于符号

bool operator < (const Node& obj) const
{
    return d<obj.d;  //从小到大排序
}

  • 结构体外定义cmp函数

int cmp(const Node&a,const Node&b){
    return a.d<b.d;//从小到大排序
}

多属性按优先级排序

int cmp(Node& a,Node& b) {
    if(a.name!=b.name) //最高优先级
        return a.name<b.name;
    else if(this.id!=o.id)
        return a.id-b.id;
    else
        return a.credit-b.credit;
}

图论

基本数据结构

堆优化

  • 数据结构定义

优先队列默认是大根堆

定义新的比较规则cmp, 重载priority_queue, 按dist[index]的值升序排列

//---------------------堆优化----------------------------
struct cmp{
    bool operator () (int a,int b){
        return dist[a]>dist[b];
    }
};
priority_queue<int,vector<int>,cmp> pq;
//---------------------堆优化----------------------------

 

 

 

 

 

  • 在dijkstra使用

下文所有有注释的都是相对于非堆优化, 需要增删的内容

fill(dist,dist+LEN,MAX);
dist[s]=0;
pq.push(s); //优先队列入队初始化
while(!pq.empty()){ //优先队列非空
    int u=pq.top();         //寻找最小的dist[u]
    pq.pop();
    if(vis[u]) continue;    //寻找最小的dist[u]
    vis[u]=1;
    for(int i=0;i<N;i++) if(!vis[i]){
        if(dist[u]+g[u][i]<=dist[i]){   //一定要包含等于符号!!!!
            dist[i]=dist[u]+g[u][i];
            pq.push(i); //优先队列入队
        }
    }
}

最短路径

Floyd

void print_via_ij(int i,int j){
    if(via[i][j]!=-1){
        int k=via[i][j];
        cout<<k<<"->";
        print_via_ij(k,j);
    }else{
        cout<<j<<"->";
    }
}

void floyd(){
    for(int i=0;i<N;i++){   //初始化
        for(int j=0;j<N;j++){
            if(g[i][j]==0) g[i][j]=INF;
            via[i][j]=-1;
            A[i][j]=g[i][j];
        }
    }
    for(int k=0;k<N;k++){   //临时点
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                if(A[i][j]>(A[i][k]+A[k][j])){
                    A[i][j]=A[i][k]+A[k][j];
                    via[i][j]=k;
                }
            }
        }
    }
    //输出0到各点距离
    int v=0;
    for(int i=1;i<N;i++){
        cout<<v<<"->";
        print_via_ij(v,i);
        cout<<endl;
    }
}

原生dijkstra

int N,M;
int dist[VN];
int pre[VN];
int vis[VN];

void dijkstra(int s){
    fill(dist,dist+N,INF);
    fill(pre,pre+N,-1);
    dist[s]=0;
    pq.push(s);     //优先队列初始化
    while(!pq.empty()){     //优先队列非空
        int u=pq.top();     //找到最小u点
        pq.pop();
        if(vis[u]) continue;//找到最小u点
        vis[u]=1;   //找到最近点,加入S集
        for(int i=head[u];~i;i=mp[i].next){     //链式前向星遍历
            int to=mp[i].to;    //链式前向星: u 的后继点 to
            int w=mp[i].w;      //链式前向星: u -> to 边权
            if(!vis[to]){   //松弛
                if(dist[to] >= dist[u]+w) { //s->to >= s->u->to,一定要包含等于符号
                    dist[to] = dist[u] + w;
                    pre[to]=u;
                    pq.push(to);
                }
            }
        }
    }
}

 

 

 

 

 

最短路计数

int pathc[VN];  //最短路条数

void dijkstra(int s,int e){
    pathc[s]=1;     //初始化源点最短路条数
    ...................

if(!vis[to]){   //松弛
    if(dist[to] > dist[u]+w) { //s->to > s->u->to
        pathc[to] = pathc[u];
        //其余公操作
    }else if(dist[to]==dist[u]+w){
        pathc[to] += pathc[u];
        //其余公操作
    }
}

在有多条最短路的情况下, 寻找点权途经点权最大的最短路

int vw[VN];    //点权 vertex weight
int by_vw[VN];   //途经点权 bypass vertex weight

void dijkstra(int s,int e){
    by_vw[s]=vw[s];//初始化源点途经点权
    ...................

if(!vis[to]){   //松弛
    if(dist[to] > dist[u]+w) { //s->to > s->u->to
        by_vw[to]=by_vw[u]+vw[to];
        //其余公操作
    }else if(dist[to]==dist[u]+w){
        if(by_vw[u]+vw[to] > by_vw[to]){
            by_vw[to]=by_vw[u]+vw[to];
        }
    }
}

 

 

 

 

 

 

 

最小生成树

并查集优化Kruskal

//--------------------------并查集-----------------------------
int fa[LEN];
int init(){
    int i;
    FF(i,LEN) fa[i]=i;
}
int findFa(int x){
    if(x==fa[x]) return x;
    int r=x;
    while(r!=fa[r]){//找到根节点
        r=fa[r];
    }
    int t=x;
    while(x!=fa[x]){//路径压缩
        t=fa[x];
        fa[x]=r;
        x=t;
    }
    return r;
}
void Union(int a,int b){
    int pa=findFa(a);
    int pb=findFa(b);
    fa[pa]=pb;
}
//--------------------------边表-----------------------------
typedef struct Edge{//边表
    int u,v,w;//两点和边权
    //按照边权对边表进行排序
    bool operator < (const Edge& obj) const
    {
        return w<obj.w;  //从小到大排序
    }
}Edge;
Edge edge[200010];

 

 

 

 

 


//--------------------------主函数-----------------------------
init();         //一定要初始化,不然拉闸
int cnt=0,mst=0;
FF(i,M){
    scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+M);
FF(i,M){
    Edge &e=edge[i];
    //Union操作
    int pa=findFa(e.u);
    int pb=findFa(e.v);
    if(pa==pb)
        continue;
    fa[pa]=pb;
    //更新MST
    mst+=e.w;
    cnt++;
}
if(cnt==N-1){
    printf("%d\n",mst);
}else{
    printf("orz\n");
}

数据结构

并查集

int fa[LEN];
int init(){
    int i;
    FF(i,LEN) fa[i]=i;
}
int findFa(int x){
    if(x==fa[x]) return x;
    int r=x;
    while(r!=fa[r]){//找到根节点
        r=fa[r];
    }
    int t=x;
    while(x!=fa[x]){//路径压缩
        t=fa[x];
        fa[x]=r;
        x=t;
    }
    return r;
}
void Union(int a,int b){
    int pa=findFa(a);
    int pb=findFa(b);
    fa[pa]=pb;
}

二叉树的各类题型

https://www.cnblogs.com/TQCAI/p/8546737.html

根据前序+中序生成树

Node* build_tree(int ps,int pe,int is,int ie){//Node* root=build_tree(0,n-1,0,n-1);
    if(ps>pe) return NULL;
    if(ps==pe) return new Node(in[is]);
    int i=is;
    while(i<=ie && in[i]!=pre[ps]) i++;
    Node * node=new Node(in[i]);
    int dLeft=i-is;    //左侧元素数量
    node->l=build_tree(ps+1,ps+dLeft,is,is+dLeft-1);
    node->r=build_tree(ps+dLeft+1,pe,i+1,ie);
    return node;
}

根据后序+中序生成树

Node* build_tree(int ps,int pe,int is,int ie){//Node* root=build_tree(0,n-1,0,n-1);
    if(ps>pe) return NULL;
    if(ps==pe) return new Node(in[is]);
    int i=is;
    while(i<=ie && in[i]!=post[pe]) i++;
    Node * node=new Node(in[i]);
    int dLeft=i-is;    //左侧元素数量
    node->l=build_tree(ps,ps+dLeft-1,is,is+dLeft-1);
    node->r=build_tree(ps+dLeft,pe-1,i+1,ie);
    return node;
}

 

 

 

 

 

 

 

根据前序+中序生成后序:

//caution: should using initialize code: " t=0; "
//pre + in -> post
//        pre_start pre_end in_start in_end
void setPost(int ps,int pe,int is,int ie){
    if(ps>pe)return;//null
    if(ps==pe){
        post[t++]=pre[ps];
    }else{
        //find the elem is the pair of preOrder (ps)
        int i=is;
        while(in[i]!=pre[ps] && i<ie) i++;//redirect
        //left
        setPost(ps+1, ps+i-is, is, i-1);
        //right
        setPost(ps+i-is+1, pe, i+1, ie);
        //root
        post[t++]=pre[ps];
    }
}

根据后序+中序生成后序:

//caution: should using initialize code: " t=0; "
//post + in -> pre
//        post_start post_end in_start in_end
void setPre(int ps,int pe,int is,int ie){
    if(ps>pe)return;//null
    if(ps==pe){
        pre[t++]=post[ps];
    }else{
        //find the elem is the pair of preOrder (ps)
        int i=is;
        while(in[i]!=post[pe] && i<ie) i++;//redirect
        //root
        pre[t++]=post[pe];
        //left
        setPre(ps, ps+i-is-1, is, i-1);
        //right
        setPre(ps+i-is, pe-1, i+1, ie);
    }
}

 

 

 

根据前序+后序生成中序

生成任意一个二叉树

void setIn(int preS,int preE,int postS,int postE){
    if(preS>preE) return;
    if(preS==preE){
        in[t++]=pre[preS];
        return;
    }
    int i=postS;
    while(i<=postE-1 && post[i]!=pre[preS+1]) i++;
    int ln=i-postS+1;    //left_num
    if(i==postE-1){        //more than one condition
        yes=0;
        //默认找到的结点都为【左结点】。(如果想设置为“右结点”,可以改变setIn递归函数的位置)
        setIn(preS+1,preS+ln,postS,postS+ln-1);
        in[t++]=pre[preS];
        return;
    }
    setIn(preS+1,preS+ln,postS,postS+ln-1);
    in[t++]=pre[preS];
    setIn(preS+ln+1,preE,postS+ln,postE-1);
}

计算有多少种二叉树

int cnt;
void calc(int preS,int preE,int postS,int postE){
    if(preS>=preE) return;
    int i=postS;
    while(i<=postE-1 && post[i]!=pre[preS+1]) i++;
    int ln=i-postS+1;    //left_num
    if(i==postE-1) cnt++;
    calc(preS+1,preS+ln,postS,postS+ln-1);
    calc(preS+ln+1,preE,postS+ln,postE-1);
}

最后结果为pow(2,cnt)种结果

 

 

 

 

 

 

根据中序和层序来建树

  • 数据结构

typedef struct Node{
    int d;
    struct Node* l;
    struct Node* r;
    Node(int d):d(d){
        l=r=NULL;
    }
};
Node * root;
map<int,int> num2index; //求一个数在中序中的索引
int layer[LEN];
int in[LEN];

  • 核心代码

//主程序段调用
for(int i=1;i<=n;i++){    //循环变量 i 遍历整个 layer 数组
    j=1;
    while(j<=n && in[j]!=layer[i]) j++;    //找到in[j]==layer[i]
    num2index[in[j]]=j; //这个数在中序中的索引
    build_tree(root,j);
}
//建树函数
void build_tree(Node* & root,int i){// index of in
    if(!root){
        root=new Node(in[i]);
        return;
    }
    int ri=num2index[root->d];    //root_index;
    if(i<ri) build_tree(root->l,i);
    else     build_tree(root->r,i);
}

Huffman树

https://www.cnblogs.com/TQCAI/p/8538920.html

  • 数据结构定义

int arr[100];
typedef struct Node{
    int d,l,r,i;
    Node(int d=0):d(d){
        l=-1;
        r=-1;
    }

 


    bool operator < (const Node& obj) const
    {
        return d>obj.d;  //因为要装入到大根堆的优先队列中
    }
}Node;

Node nodes[100];//静态结点

  • 初始化

    int i,n=0;
    while(~scanf("%d",&i)){
        nodes[n]=Node(i);
        nodes[n].i=n;  //类似并查集,如果没有父结点,父结点就是自己
        pq.push(nodes[n]);
        n++;
    }

  • 建立Huffman树

    while(pq.size()>1){  //最后只剩下一个结点
        //从优先队列中拿出两个最小的结点
        if(pq.empty())
            break;
        Node a=pq.top();
        pq.pop();
        if(pq.empty())
            break;
        Node b=pq.top();
        pq.pop();
        //构造一个新的结点
        Node c(a.d+b.d);
        c.l=a.i;
        c.r=b.i;
        nodes[n]=c;  //放到新的静态区域中,并对n更新
        c.i=n;
        n++;
        //把新的结点放入优先队列中
        pq.push(c);
    }

线段树

更新:区间加,查询:区间和

https://www.luogu.org/problemnew/show/P3372

#define LSON LS(p),l,mid
#define RSON RS(p),mid+1,r
#define LS(x) x<<1
#define RS(x) x<<1|1
#define MS ll mid=(l+r)>>1

ll a[LEN];
ll tree[LEN*4];  //线段树
ll lazy[LEN*4];  //lazy优化

inline void push_up(ll p){
    tree[p]=tree[LS(p)]+tree[RS(p)];
}

void build(ll p,   ll l,   ll r){
    lazy[p]=0;
    if(l==r){
        tree[p]=a[l];
        return;
    }
    MS;
    build(LSON);
    build(RSON);
    push_up(p);
}

inline void f(ll p,ll l,ll r,ll d){
    lazy[p]+=d;
    tree[p]+=d*(r-l+1);
}

inline void push_down(ll p,ll l,ll r){
    ll mid=(l+r)>>1;
    f(LSON,lazy[p]);
    f(RSON,lazy[p]);
    lazy[p]=0;
}

//update(nl,nr,1,1,N,d)
inline void update(ll nl,ll nr,/*固定域*/ ll p, ll l, ll r,/*查找域*/ ll d){
    if(nl<=l && r<=nr){
        tree[p]+=d*(r-l+1);
        lazy[p]+=d;
        return;
    }
    push_down(p,l,r);
    MS;
    if(nl<=mid)
        update(nl,nr,LSON,d);
    if(mid<nr)  // (mid+1)<=nr
        update(nl,nr,RSON,d);
    push_up(p);
}

//query(nl,nr,1,1,N)
ll query(ll nl,ll nr,/*固定域*/ ll p, ll l, ll r/*查找域*/){
    ll res=0;   //add
    if(nl<=l && r<=nr)
        return tree[p]; //modify
    push_down(p,l,r);
    MS;
    if(nl<=mid)
        res+=query(nl,nr,LSON); //modify
    if(mid<nr)
        res+=query(nl,nr,RSON); //modify
    return res; //add
}

 

搜索法

连通增量

4连通增量

int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};

8连通增量

int dir[8][2];
int n;

void build_dir()
{
    int k=0;
    FF(i,3)FF(j,3){
        if(i==1 && j==1) continue;
        dir[k][0]=i-1;
        dir[k][1]=j-1;
        k++;
    }
}

DFS

for外夹紧的DFS

//调用
dfs(s);

 

//dfs
void dfs(int s){//当进入dfs(s)时,0~s-1的状态都已经完成
    //退出区
    if(s==e){ //终态。也可以在退出区剪枝
        return;    //一定要有返回
    }
    //入栈区
    path.push_back(s);
    vis[s]=1;
    //遍历区
    for(i){
        dfs(i);
    }
    //出栈区
    vis[s]=0;
    path.pop_back();
}

for内夹紧的DFS

//调用
vis[s]=1;//注意,如果初态不同,可能还会有循环
dfs(s);
vis[s]=0;
//dfs
void dfs(int s){
    if(s==e){   //终态判断也可以写在for内
        return;    //一定要有返回
    }
    for(i){
        path.push_back(i);
        vis[i]=1;
        dfs(i);
        vis[i]=0;
        path.pop_back();
    }
}

排列树

void backtrack(int t) { //坑: 第一个结果并不是字典序最小结果
    if (t > N){
        //终态
    }else
        for (int i = t; i<=N; i++){
            swap(x[t], x[i]);
            if(/*剪枝条件 && 限制条件*/) backtrack(t+1);
            swap(x[t], x[i]);
        }
}

  • 自带的全排列

do{
    //终态
}while(next_permutation(arr,arr+n));    //n!

BFS

一般的BFS

void bfs(){
    queue<node> q;
    q.push(S);
    vis[S]=1;//入队标记
    bool isFind=false;//终态标记
    while(!q.empty()){
        int sz=q.size();
        while(sz--){    //方便计算层数
            node u=q.front();
            q.pop();
            if(u==E){ //终态
                isFind=true;
                goto END;
            }
            FF(i,N){    //对于N种情况进行分析
                if(!vis[i]){    //入队判断
                    vis[i]=1;
                    q.push(Node[i]);    //入队标记
                }
            }
        }
        step++; //bfs层数
    }
    END:    //cpp用goto跳出多重循环,Java有其他法
}

排列组合

排列序列A(n,m)

int a[LEN];     //排列前的序列
int vis[LEN],cur[LEN];   //完成一次排列后形成的序列


void permutations(int t,int n,int m){//A(n,m)
    if(t>=m){   //达到循环上限
        for(int i=0;i<m;i++)  //输出一次排列后的序列
            printf("%d ",cur[i]);
        printf("\n");
        return;
    }else{
        for(int i=0;i<n;i++)if(!vis[i]){
            vis[i]=1;
            cur[t]=a[i];
            permutations(t+1,n,m);
            vis[i]=0;
        }
    }
}
//调用
permutations(0,n,m);

笛卡尔积P(n,m)

string a="1234";//递归前
vector<string> ans;//递归结果

string ch2str(char fuck){
    char fuckYou[2]={0};
    fuckYou[0]=fuck;
    return fuckYou;
}

void product(int t,int n,int m){//P(n,m)
    if(t>=m){//达到循环上限
        for(int i=0;i<ans.size();i++)
            printf("%s\n",ans[i].c_str());

        return;
    }else{
        vector<string> tmp;
        int sz=ans.size();
        if(sz){
            for(int i=0;i<n;i++){
                for(int j=0;j<sz;j++){
                    tmp.push_back(ans[j]+a[i]);
                }
            }
        }else{
            for(int i=0;i<n;i++){
                tmp.push_back(ch2str(a[i]));
            }
        }
        ans=tmp;
        product(t+1,n,m);
    }
}

常用自带库与函数

C语言字符串函数

//拷贝
char *strcpy(char *dest, const char *src)
//比较
int strcmp(const char *str1, const char *str2)
/*
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2。
*/
//拼接
char *strncpy(char *dest, const char *src, size_t n)
    //->实例
    char src[10]="1234567";
    char dest[10];
    int start=2;
    int length=3;
    strncpy(dest,src+start,length);
    dest[length]=0;//制作终止符

其他

  • 内存赋值

memset(void *s,int ch,size_t n);

==注意==:

对于在源程序段声明的数组,可以用sizeof直接获得字节大小。对于函数内声明的数组不行。

只能对字节进行赋值,常用的有0和1 。

algorithm

  • accumulate求和

对于数组a,对于区间[0,N),进行求和,之后加上0

int sum=accumulate(a,a+N,0);

  • distance求迭代器的下标

distance(s.begin(),it)

  • fill批量赋值

一维数组: fill(a,a+length,value);

二维数组:

    FF(i,LEN)
        fill(dp[i],dp[i]+LEN,0);

  • count计数

一种用法是判断容器内是否存在某一元素

count(a, a+length, value)

条件计数

count_if(a, a+length, condition_function)

  • search子串查找

int*p=search(seq,seq+5,sub,sub+3);

重复串查找

seq中是否有n个value

int*p=search_n(seq,seq+8,n,value);

  • 连续序列去重

数组arr为连续序列,重复的元素都相邻。使用下面语句后,去重+长度更新

n=unique(arr,arr+n)-arr;

  • copy复制

用法1

//将数组myints中的七个元素复制到myvector容器中
copy ( myints, myints+7, myvector.begin() );//调用这行代码之前,必须用  myvector.resize(7)

用法2 数组平移

copy(arr+1,arr+9+1,arr);//把长度为9的数组向左平移1位

  • for_each对于每个迭代器执行指定函数

对于迭代器

// 3 for_each
 void fun(map<int,string>::reference a)  //不要少了reference,不然会报错。
{
    a.second+="_suffix";    //a是引用对象。
    cout<<a.first<<" "<<a.second<<endl;
}
for_each(m.begin(),m.end(),fun);

对于容器

void fun1(int & p){  //回调函数的参数同样也是引用类型
    p++;
}

  • equal判断两个序列是否完全相等

bool ans=equal(A,A+LEN,B);

  • mismatch找到两个序列第一次失配的元素

pair<int*,int*>ans=mismatch(A,A+LEN,B);

  • rotate循环平移

向左移动n位

rotate(A,A+n,A+LEN);

向右移动n位

rotate(A,A+LEN-n,A+LEN);

  • swap_ranges交换两个区间的值

swap_ranges(A,A+5,B);

  • remove删除容器中的某个值

// 把要删除的元素移动到容器末尾
it=remove(v.begin(),v.end(),removedValue)  //it为末尾删除区的第一个元素
// 真正意义上的删除
v.erase(remove(v.begin(),v.end(),removedValue),v.end());

  • transform对容器中的序列套接一个函数,把结果放到一个容器

transform(A,A+LEN,B,add);

  • replace把序列中的某个值替换为另一个值

replace(std::begin(data), std::end(data), pre, after);

string

基本操作

初始化

string(int n,char c); //用n个字符c初始化

取子串

string substr(int pos=0, int n=npos) const;    //返回由pos开始的n个字符组成的子字符串

查找

==注意!== find函数如果查找不到,就返回string::npos

int find(char c,int pos=0) const;  //从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos=0) const;  //从pos开始查找字符串s在当前字符串的位置
int find(const string &s, int pos=0) const;  //从pos开始查找字符串s在当前字符串中的位置
find函数如果查找不到,就返回 string::npos
int rfind(char c, int pos=npos) const;   //从pos开始从后向前查找字符c在当前字符串中的位置
int rfind(const char *s, int pos=npos) const;
int rfind(const string &s, int pos=npos) const;
//rfind是反向查找的意思,如果查找不到, 返回npos

替换

string &replace(int pos, int n, const char *s);//删除从pos开始的n个字符,然后在pos处插入串s
string &replace(int pos, int n, const string &s);  //删除从pos开始的n个字符,然后在pos处插入串s
void swap(string &s2);    //交换当前字符串与s2的值

插入

string &insert(int pos, const char *s);
string &insert(int pos, const string &s);
//前两个函数在pos位置插入字符串s
string &insert(int pos, int n, char c);  //在pos位置 插入n个字符c

删除
string &erase(int pos=0, int n=npos);  //删除pos开始的n个字符,返回修改后的字符串
/*
注意string的erase与其他容器的erase不同。其他容器的erase删除迭代器,返回删除元素的第一个后级元素
*/

大小写转换

transform(s.begin(), s.end(), s.begin(), ::toupper);//转大写
transform(s.begin(), s.end(), s.begin(), ::tolower);//转小写

常见字符串ASCII值

'0'=48  'a'=97  'A'= 65

插入字符串

void insertStr(string &str,const string &obj,int pos=0){
    str.replace(pos,0,obj);
}

全部替换

bool replaceAll(string &str,const string &a,const string &b){
    int al=a.length();
    int bl=b.length();
    int pos,pre=0;
    bool isFind=false;

 

    while((pos=str.find(a,pre))!=string::npos){
        str.replace(pos,al,b);
        pre=pos+bl;
        isFind=true
    }
    return isFind;
}

字符串拆分

  • 单字符切割简单字串

void split(string s,char token,vector<string>& v){
    int n=s.length();
    string t;
    FF(i,n){
        char c=s[i];
        if(c!=token)
            t+=c;
        else{
            v.push_back(t);
            t="";
        }
    }
    v.push_back(t);
}

  • 多字符切割简单字串

void split(string s,string token,vector<string>& v){
    int n=s.length();
    int m=token.length();
    string t;
    int i=0;
    while(i+m<=n){
        int step=1;
        char c=s[i];
        if(s.substr(i,m)!=token)
            t+=c;
        else{
            v.push_back(t);
            t="";
            step=m;
        }
        i+=step;
    }
    v.push_back(t);
}

priority_queue

基本操作

入队:push()

出队:pop()

队顶:top()

小根堆

pq默认为大根堆(见严蔚敏的大根堆和堆排序),但是如果我们想用小根堆:

priority_queue<int,vector<int>,greater<int> > pq;

结构体小根堆

//根据d的值有小到大
typedef struct Node{
    int d;
    bool operator < (const Node& obj) const
    {
        return d>obj.d;  //注意
    }
}Node;
priority_queue<Node> pq;

外部小根堆

struct cmp{
    bool operator () (int a,int b){
        return dist[a]>dist[b];
    }
};
priority_queue<int,vector<int>,cmp> pq;

set

基本操作

set:红黑树实现

multiset:多值集合,但能动态排序

初始化

int num[]={1,2,3};
multiset<int> s(num,num+3);

插入:insert()

计数:count() 也可以判断某个item存在与否

清空:clear()

查找:it=find(s.begin(),s.end(),v); 或者it=s.find(v)

获取下标:distance(s.begin(),it)

查找失败:it==s.end()

二分查找:

[a,b)

s.lower_bound(v) s.upper_bound(v)

删除

s.erase(it)或s.erase(it_begin,it_end)

注意,set的迭代器没有实现+操作符,所以只能循环使用++或--来对下标进行定位

遍历

 multiset<int>::iterator it=s.begin();
 while(it!=s.end()){
     printf("%d",*it);
     it++;
     if(it!=s.end())
         printf(" ");
 }

改为从大到小排序

使用STL自带重载

set<int,greater<int> > s;

自己写个

struct cmp{
    bool operator() (const int& a,const int& b) const
    {
        return a>b;
    }
};
set<int,cmp> s;

 

结构体放入

typedef struct Node{
    int d;
    Node(int d):d(d){}
    bool operator < (const Node& obj) const
    {
        return d<obj.d;  //从小到大排序
    }
}Node;

vector

动态删除所有满足条件的元素

while(it!=v.end()){
    if(*it==3){//满足了某一条件,则删除
        it=v.erase(it);
    }else{//不满足,自增
        it++;
    }
}

数学问题

表达式求值

https://blog.csdn.net/TQCAI666/article/details/88692096

  • 用一个结构体记录数组或操作符

typedef struct Node{//运算结点,记录数字或运算符号
    bool isop;
    char op;
    int num;
    Node(){}
    Node(int d){
        num=d;
        isop=0;
    }
    Node(char op_){
        op=op_;
        isop=1;
    }
    void output(){
        if(isop) cout<<op;
        else cout<<num;
    }
}Node;

中缀表达式生成后缀表达式

int getPriority(char op){
    switch(op){
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        default:
            return 0;
    }
}

vector<Node> yieldPostOrderExpress(vector<Node> &in){
    vector<Node> post;
    Node ops[LEN];
    int top=-1;//栈顶指针
    FF(i,in.size()){
        Node u=in[i];
        if(u.isop){
            if(u.op=='('){
                ops[++top]=u;//入栈
            }else if(u.op==')'){
                while(top>=0){
                    Node p=ops[top--];
                    if(p.op=='('){
                        break;
                    }else{
                        post.push_back(p);
                    }
                }
            }else{
                while(top>=0 && getPriority(u.op) <= getPriority(ops[top].op)){
                    post.push_back(ops[top--]);//持续出栈直到满足优先级
                }
                ops[++top]=u;//入栈
            }

        }else{
            post.push_back(u);
        }
    }
    //处理剩下的栈
    while(top>=0){
        post.push_back(ops[top--]);
    }
    return post;
}

后缀表达式计算实值

Node operate(Node &a,Node& b,char op){
    int v;
    switch(op){
    case '+':
        v=a.num+b.num;
        break;
    case '-':
        v=a.num-b.num;
        break;
    case '*':
        v=a.num*b.num;
        break;
    case '/':
        v=a.num/b.num;
        break;
    }
    return Node(v);
}

int calcPostOrderExpress(vector<Node>& post){
    Node s[LEN];
    int top=-1;
    FF(i,post.size()){
        Node& u=post[i];
        if(u.isop){
            Node a=s[top--];
            Node b=s[top--];
            Node ans=operate(a,b,u.op);
            s[++top]=ans;
        }else{
            s[++top]=u;
        }
    }
    return s[0].num;
}

 

 

 

 

 

进制转换

实值转二进制序列

string getBinarySequence(int num){
    string ans="";
    while(num){
        if(num&1){
            ans="1"+ans;
        }else{
            ans="0"+ans;
        }
        num>>=1;
    }
    return ans;
}

R进制序列转实值

int getTrueValue(int r,string seq){//radix sequence
    int n=seq.size();
    int base=1;
    int ans=0;
    for(int i=n-1;i>=0;i--){
        int v=seq[i]-'0';
        ans+=v*base;
        base*=r;
    }
    return ans;
}

排列组合

记忆化搜索计算组合数

int C[50][50];

int C_(int n,int m){
    if(C[n][m]) return C[n][m];
    if(m==0){
        C[n][m]=1;
        return C[n][m];
    }
    if(m==n){
        C[n][m]=1;
        return C[n][m];
    }
    else{
        C[n][m]=C_(n-1,m-1)+C_(n-1,m);
        return C[n][m];
    }
}

快速幂

常数快速幂

ll quickPower(ll a,ll b){
    ll ans=1,base=a;
    while(b>0){
        if(b&1)
            ans*=base;
        base*=base;
        b>>=1;
    }
    return ans;
}

快速幂取余

性质

 

ll quickPowerMod(ll a,ll b,ll m){
    ll ans=1,base=a;
    while(b>0){
        if(b&1){
            ans*=base;
            ans%=m;
        }
        base*=base;
        base%=m;
        b>>=1;
    }
    return ans%m;
}

高精度快速幂

hp ans("1");
    hp base("2");   //2^p
    while(P>0){
        if(P&1)
            ans=multiplyh(ans,base);
        base= multiplyh(base,base);
        P>>=1;
    }

 

矩阵快速幂

  • 矩阵乘法

matrix* multi(matrix& A, matrix& B){
    matrix* C=new matrix;
    C->row=A.row;
    C->col=B.col;
    for(int i=0;i<A.row;i++)
        for(int j=0;j<B.col;j++)
            for(int k=0;k<A.col;k++)
                C->m[i][j]+=A.m[i][k]*B.m[k][j];
    return C;
}

  • 矩阵快速幂

matrix quickPower(matrix& A,int p){    //A^p
    int n=A.col;
    //得到单位阵
    matrix ans(n,n);//ans=E
    FF(i,n)
        FF(j,n)
            if(i==j)
                ans.m[i][j]=1;
            else
                ans.m[i][j]=0;
    //开始快速幂
    matrix base=A;
    while(p){
        if(p&1)
            ans=*multi(ans,base);
        base=*multi(base,base);
        p>>=1;
    }
    return ans;
}

数论

gcd

int gcd(int a,int b){
    while( b != 0){
        int t = a % b;
        a = b;
        b = t;
    }
    return a;
}

素数判断

bool isPrime(int x){
    if(x<=1) return 0;
    for(int i=2;i<=sqrt(x);i++){
        if(x%i==0) return 0;
    }
    return 1;
}

欧拉线性筛


#define MAXN 100005     //素数的最多个数
#define MAXL 1299710    //要求的最大素数
//实际操作中,只需要控制MAXL的长度,MAXN开的足够大即可
int prime[MAXN];//素数表
int check[MAXL];//如果n是素数,则check[n]=0,否则check[n]=1

void EulerSieveMethod(){
    int tot = 0;
    memset(check, 0, sizeof(check));
    for(int i=2;i<MAXL;i++){
        if(check[i]==0){//素数
            prime[tot++]=i;
        }
        for(int j=0;j<tot;j++){
            int v=i*prime[j];
            if(v>MAXL) break;
            check[v]=1;
            if(i%prime[j]==0)//有点没搞明白
                break;
        }
    }
}

动态规划

背包问题

01背包

https://www.luogu.org/problemnew/show/P1048

  • 对于==刚好装满==问题

采用-INF对f数组进行初始化

fill(f,f+FN,-INF);

需要注意的是,INF应该采取最大的安全值,例如:#define INF ((1<<30)-1)

f[0]初始化为0

对结果进行判断

        if(ans<0)
            puts("NO");
        else
            printf("%d\n",ans);

  • 原生方法

    for(int i = 1; i <= N; i++){      //i代表物品
        for(int j = 0; j <= M; j++){  //j代表当前背包容量上限
            if(j>=w[i]){               //当前物品能放入
                f[i][j]=max(f[i-1][j],          //不放
                            f[i-1][j-w[i]]+v[i]);  //放
            }else{
                f[i][j]=f[i-1][j];
            }
        }
     }
    printf("%d", f[N][M]);

  • 滚动数组优化

    for(int i=1;i<=N;i++){
        for(int c=M;c>=w[i];c--){
            f[c]=max(f[c],f[c-w[i]]+v[i]);
        }
    }
    printf("%d\n",f[M]);

完全背包

http://nyoj.top/problem/311

  • 滚动数组优化

        for(int i=1;i<=N;i++){
            for(int c=w[i];c<=M;c++){
                f[c]=max(f[c],f[c-w[i]]+v[i]);
            }
        }

线性DP

最长不下降子序列LIS

  • N2复杂度

void DP_n2(){
    int p;  //LIS 结束的下标
    int ans=0;
    F(i,n){
        dp[i]=1;  //初始化
        F(j,i-1){
            if(a[j]<a[i])
                dp[i]=max(dp[i],dp[j]+1);
        }
        if(dp[i]>ans){  //更新最优解
            ans=dp[i];
            p=i;
        }
    }
}

分析:根据DP数组的线性递增顺序就能找到所有的LIS序列

ans=5 p=11
//序列 :
1 3 7 6 8 5 3 2 7 2 9 
//DP数组 :
1 2 3 3 4 3 2 2 4 2 5 

  • Nlog2N复杂度

不严格递增(不下降)

void DP_nLogn1(){
    int top=0;
    F(i,n){
        if(top==0 || a[i]>=dp[top-1])
            dp[top++]=a[i];
        else{
            int pos=upper_bound(dp,dp+top,a[i])-dp;
            dp[pos]=a[i];
        }
    }
}

严格递增

void DP_nLogn2(){
    int top=0;
    F(i,n){
        if(top==0 || a[i]>dp[top-1])
            dp[top++]=a[i];
        else{
            int pos=lower_bound(dp,dp+top,a[i])-dp;
            dp[pos]=a[i];
        }
    }
}

最长公共子序列

void LCS(){
    F(i,N){
        F(j,M){
            if(a[i]==b[j])
                dp[i][j]=dp[i-1][j-1]+1;
            else
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
}

最大子段和

    int i,p=0,ans=-MAX,t,n;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%d",&t);
        p+=t;
        ans=max(ans,p);
        if(p<0) p=0;  //说明当前的t是负数, 从下个t开始初始化
    }
    printf("%d\n",ans);

区间DP

加分二叉树

    F(i,N){
        cin>>f[i][i];
        f[i][i-1]=1; //空结点为0
        root[i][i]=i;
    }
    f[N+1][N]=1;
    for(int d=1;d<N;d++){//间隔
        for(int i=1;i+d<=N;i++){//开始
            int j=i+d;//结束
            root[i][j]=i;
            f[i][j]=f[i][i]+f[i+1][j];
            for(int k=i+1;k<=j;k++){ //分割
                int tmp_fij=f[k][k]+f[i][k-1]*f[k+1][j];
                if(tmp_fij>f[i][j]){
                    f[i][j]=tmp_fij;
                    root[i][j]=k;
                }
            }
        }
    }
    cout<<f[1][N]<<endl;

最长回文子串

同样用到了区间DP的思想,通过逐渐增大排查范围,无后效性

char buf[LEN];
int dp[LEN][LEN];

int main()
{
    fgets(buf,LEN,stdin);
    int N=strlen(buf);
    //初始化。1表示合法
    FF(i,N) dp[i][i]=1;
    int ans=0;
    FF(i,N-1)
        if(buf[i]==buf[i+1])
            dp[i][i+1]=1;
    for(int v=2;v<N;v++){//v表示间隔
        for(int i=0;i+v<N;i++){//i表示开始下标
            int j=i+v;
            if(buf[i]==buf[j] && dp[i+1][j-1]==1){//判定为合法
                dp[i][j]=1;
                ans=max(ans,v);
            }
        }
    }
    printf("%d",ans+1);
    return 0;
}

分治递归

归并排序求逆序对

ll tmp[LEN],a[LEN];
ll ans=0;
ll arr[LEN];

void merge(ll s,ll mid,ll e){
    ll i=0,a=s,b=mid+1;
    while(a<=mid && b<=e){
        if(arr[a]<=arr[b]){ //巨坑,<=
            tmp[i++]=arr[a++];
        }else{
            tmp[i++]=arr[b++];
            ans+=mid-a+1;
        }
    }

    while(a<=mid) tmp[i++]=arr[a++];
    while(b<=e) tmp[i++]=arr[b++];
    FF(j,i) arr[s+j]=tmp[j];
}

void mergeSort(ll s,ll e){
    if(s<e){    //能够被划为。如果只有一个数就不能被划分
        ll mid=(s+e) /2;
        mergeSort(s,mid);
        mergeSort(mid+1,e);
        merge(s,mid,e);
    }
}

台阶问题

假设有N级台阶,一个人一次可以走[1,K]级,问一共有多少种走法

相当于一次可以走1、 2步的斐波那契问题的推广

    f[0]=1;//初始化第一步
    for(int i=1;i<=N;i++){//每一级台阶
        for(int j=1;j<=K && i-j>=0 ;j++){//走j步,j∈[1,K]
            f[i]+=f[i-j];
        }
    }
    cout<<f[N];

传球游戏

有N个人,其中一个人可以从他的左边或者右边接到球。游戏开始,某人发球,求进行M次游戏后,球重新回到他的手中的传球方法数。

实则为dp

状态转移:f[i][k]=f[i-1][k-1]+f[i+1][k-1],(i=1或n时,需单独处理)

    f[1][0]=1;//fij表示在第j轮,球传到i手中的方法数。
    for(int i=1;i<=m;i++)//传 m 次
    {
        f[1][i]=f[2][i-1]+f[n][i-1];    //第一个
        f[n][i]=f[1][i-1]+f[n-1][i-1];    //最后一个
        for(int j=2;j<n;j++)            //中间
            f[j][i]=f[j-1][i-1]+f[j+1][i-1];
    }//注意第一个人和最后一个人的单独处理
    printf("%d",f[1][m]);

 

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值