ACM板子

2020/09/10 ACM 共 12684 字,约 37 分钟
call me JC

1,常见板子

素数

bool isPrime(int n)
{
  if (n == 1)
      return false;
  if (n == 2 || n == 3)
      return true;
  if (n % 6 != 1 && n % 6 != 5)
      return false;
  for (int i = 5; i <= sqrt(n); i += 6)
  {
      if (n % i == 0 || n % (i + 2) == 0)
          return false;
  }
  return true;
}

最大公约数:辗转相除法

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

快速幂:

\[pow\_mod(x,n,mod) = \begin{cases} 1, &n=0\\ pow\_mod(x*x\%mod,n/2,mod), & \text{if }n\text{ is even} \\ pow\_mod(x*x\%mod,n/2,mod)*x\%mod, & \text{if }n\text{ is odd} \end{cases}\]
ll pow_mod(ll x, ll n, ll mod) {
    if (n == 0)
        return 1;
    ll res = pow_mod(x * x % mod, n / 2);
    if (n & 1)
        res = res * x % mod;
    return res;
}

冒泡排序:

void bublesort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

运算符重载

struct Grade{
  string name;
  string id;
  int score;
  bool operator < (const Grade t) const{
      // return score < t.score 升序
      return t.score < score;//降序
  }
};

KMP:字符串匹配

字符串匹配KMP板子:

#include <iostream>
#include<string>
using namespace std;

void get_next(string T, int next[]){
    int i=0, j=-1;
    next[0] = -1;
    while(i<T.length()-1){
        if(j==-1 || T[i] == T[j]){
            i++; j++;
            next[i] = j; //当pi = pj, 'p0...pk-1pk' = 'pj-k...pj', next[j+1] = k+1, 即next[j+1] = next[j]+1
        }
        else    
            j = next[j]; //Ti != Tj, j=next[j];
    }
}

int index_KMP(string S, string T, int next[]){
    int i=0, j=0;
    // cout << (-1<int(T.length())) << endl;
    // cout << (-1<t.length())
    while(i<int(S.length()) && j<int(T.length())){
        if(j==-1 || S[i]==T[j]){
            i++; j++; //继续比较后面的字符
        }
        else{
            j = next[j]; //模式串向右移动
        }
    }
    if(j>=T.length())
        return i-T.length();//匹配成功
    else
        return 0; //匹配失败
}

int main()
{   
    int next[100];
    string s = "babaabcaba", t = "abaabcaba";   
    get_next(t, next);
    cout << index_KMP(s,t,next);
    return 0;
}

线段树:

操作格子

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>

using namespace std;

struct SegTree{
    int l, r, Sum, Max;
}seq[1000001];

void init(int l,int r, int i){//l表示左端点,r表示右端点, i表示序号
    seq[i].l = l;
    seq[i].r = r;
    seq[i].Max = 0;
    seq[i].Sum = 0;
    if(l!=r){
        int mid = (l+r)/2;
        init(l,mid, i*2);
        init(mid+1, r, i*2+1);
    }
}

void insert(int root, int i, int val){
    if(seq[root].l == seq[root].r){
        seq[root].Max = val;
        seq[root].Sum = val;
        return;
    }
    int mid = (seq[root].l + seq[root].r)/2;
    if(i <= mid)
        insert(root*2,i, val);
    else
        insert(root*2+1, i, val);
    seq[root].Max = max(seq[root*2].Max, seq[root*2+1].Max);
    seq[root].Sum = seq[root*2].Sum + seq[root*2+1].Sum;
    
}

int find_max(int x, int y, int i){
    if(x==seq[i].l && y==seq[i].r)
        return seq[i].Max;
    int mid = (seq[i].l + seq[i].r)/2;
    if(x > mid)
        return find_max(x,y,i*2+1);
    else if(y<=mid)
        return find_max(x,y,i*2);
    else
        return max(find_max(x,mid,i*2), find_max(mid+1,y, i*2+1));
}

int find_sum(int x, int y, int i){
    if(x==seq[i].l && y==seq[i].r)
        return seq[i].Sum;
    int mid = (seq[i].l + seq[i].r)/2;
    if(x > mid)
        return find_sum(x,y,i*2+1);
    else if(y<=mid)
        return find_sum(x,y,i*2);
    else
        return find_sum(x,mid,i*2) + find_sum(mid+1,y, i*2+1);
}

int main(){
    int n,m,weight, p, x, y;
    scanf("%d%d", &n, &m);
    init(1,n,1);
    for(int l1=1; l1<=n; l1++){
        scanf("%d", &weight);
        insert(1,l1,weight);
    }
    for(int l1=1; l1<=m; l1++){
        scanf("%d%d%d", &p, &x, &y);
        if(p==1){
            insert(1,x,y);
        }
        else if(p==2){
            printf("%d\n", find_sum(x,y,1));
        }
        else{
            printf("%d\n", find_max(x,y,1));
        }
    }
    return 0;
}

并查集

LeetCode:连通网络的操作次数

PAT 1001 Battle Over Cities - Hard Version

class DisjointSet {
public:
    vector<int> parent;
    int count;
    DisjointSet(int maxn) {
        parent.resize(maxn + 1);
        count = maxn;
        for (int i = 0; i < maxn; i++) {
            parent[i] = i;
        }
    };
    
    //查找根节点
    int find(int x) {
        while (x != parent[x]) {
            //路径压缩:隔代压缩
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    };
    
    //合并
    void merge(int x, int y) {
        int rootx = find(x);
        int rooty = find(y);
        if (rootx == rooty)
            return;
        parent[rootx] = rooty;
        count--;
    };
};

cout 二进制、十进制、八进制、十六进制:

#include<iostream>
#include<bitset>
using namespace std;

int main(){
  //bi -> dec
  bitset<14> biNum("101001101101111");
  cout << biNum.to_ullong() << endl;
  //dec -> bi
  cout << bitset<14>(i) << endl;
  //dec -> hex
  cout << hex << a << endl;
  //dec -> oct
  cout << oct << a << endl;
  return 0;
}

二进制枚举:

李白打酒加强版:

//二进制枚举:超时
#include<iostream>
using namespace std;

int main()
{
  int n,m;
  cin >> n >> m;
  int ans = 0;
  for(int i=0; i<(1<<(n+m-1)); i++){//最后遇到花是确定的,前n+m-1个不确定
      int tot_store = 0;
      int tot_flower = 0;
      int wine = 2;
      for(int j=0; j<(n+m-1); j++){
          if(i&(1 << j)){// i的第j位为1,设为遇到的是花
              tot_flower++;
              wine--;
          }
          else{
              tot_store++;
              wine *= 2;
          }
      }
      if(tot_flower==m-1 && tot_store==n && wine==1)
          ans++;
  }
  cout << ans % 1000000007 << endl;
  return 0;
}

递归:

李白打酒加强版:

//递归:超时
#include<iostream>
using namespace std;
int n,m;
int ans = 0;
int cnt=0;

void solution(int flower, int store, int wine){
    if(flower+store >= n+m)
        return;
    if(flower==m-1 && store==n && wine==1){
        ans++;
        return;
    }
    // cout << cnt++ << endl;
    // if(cnt == (1<<13))
    //     cout << "here";
    solution(flower+1, store, wine-1);
    solution(flower, store+1, wine*2);   
}

int main()
{
    cin >> n >> m;
    solution(0,0,2);
    cout << ans % 1000000007 << endl;
    return 0;
}

动态规划:

李白打酒加强版:

//DP动态规划
#include<iostream>
using namespace std;
typedef long long ll;

ll n,m;
ll dp[202][101][101];//dp[i][j][k]: 第i个位置,遇到j个花, 还剩k斗酒 的策略数
ll mod = 1e9+7;

int main()
{
    cin >> n >> m;
    dp[0][0][2] = 1;
    for(int i=1; i<n+m; i++){
        for(int j=0; j<m; j++){
            for(int k=0; k<=m; k++){
                if(!(k&1)){//k为偶数
                    dp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k>>1]) % mod;
                }
                if(j>=1){
                    dp[i][j][k] = (dp[i][j][k] + dp[i-1][j-1][k+1]) % mod;
                }
            }
        }
    }
    cout << dp[n+m-1][m-1][1] << endl;
    return 0;
}

最长公共子序列:

int lcs(string x, string y) {
    int xlen = x.length(), ylen = y.length();
    x = " " + x; y=" "+y;//下标从1开始取
    vector<vector<int>> dp;
    dp.resize(xlen+1);
    for (int i = 0; i < xlen+1; i++)
        dp[i].resize(ylen+1);
    for (int i = 0; i <= xlen; i++)
        dp[i][0] = 0;
    for (int j = 0; j <= ylen; j++)
        dp[0][j] = 0;
    for (int i = 1; i <= xlen; i++) {
        for (int j = 1; j <= ylen; j++) {
            if (x[i] == y[j]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    return dp[xlen][ylen];
}

最长公共子序列:

int lis(string s) {
    vector<int> dp;
    dp.resize(s.size());
    dp[0] = 1;
    for (int i = 1; i < s.size();i++) {
        int maxl = 1;
        for (int j = 0; j < i;j++) {
            if (s[i] > s[j])
                maxl = max(maxl, dp[j] + 1);
            else
                maxl = max(maxl, dp[j]);
        }
        dp[i] = maxl;
    }
    return dp[s.size() - 1];
}

回溯:

n皇后问题

void init(int n){
    col.resize(n), row.resize(n), diag.resize(n*2-1), negdiag.resize(n*2-1);
    for(int i=0; i<n; i++)
        row[i] = col[i] = FREE;
    for(int i=0; i<n*2-1; i++)
        diag[i] = negdiag[i] = FREE;
};

vector<vector<string>> ans;
void printAns(int n){
    vector<string> solution;
    for(int i=0; i<n; i++){
        string tmp = "";
        for(int j=0; j<n; j++){
            if(row[i] == j){
                tmp += "Q";
            }
            else
                tmp += ".";
        }
        solution.push_back(tmp);
    }
    ans.push_back(solution);
};

void recursive(int i, int n){
    if(i==n){
        printAns(n);
        return;
    }
    for(int j=0; j<n; j++){
        if(col[j] == NOTFREE || diag[i+j] == NOTFREE || negdiag[i-j+n-1] == NOTFREE)
            continue;
        row[i] = j; col[j] = diag[i+j]=negdiag[i-j+n-1] = NOTFREE;

        recursive(i+1, n);

        row[i] = col[j] = diag[i+j] = negdiag[i-j+n-1] = FREE;
    }
};

vector<vector<string>> solveNQueens(int n) {
    init(n);
    recursive(0,n);
    return ans;
}

2, 图论

邻接矩阵表示法:

class MatGraph {
public:
    set<int> V;//顶点表
    int E[maxn][maxn];//边表
    int vn, en;//当前图的顶点数和边数
    MatGraph() {
        for (int i = 0; i < maxn; i++) {
            for (int j = 0; j < maxn; j++)
            {
                E[i][j] = -1;
            }
        }
        vn = en = 0;
    };
    
    void addV(int newV) {
        if (V.find(newV) == V.end()) {
            V.insert(newV);
            vn++;
        }
    };
    
    void addE(int begin, int end, int weight) {
        if (E[begin][end] == -1) {
            en++;
        }
        E[begin][end] = E[end][begin] = weight;
    };
};

邻接表法:

struct {
    int tovex;//该边指向的顶点的位置
    edgeNode* next;//下一条边的指针
}edgeNode;

struct {
    int data;//定点信息
    edgeNode* first;//指向第一条依附于该定点的边的指针
}vexNode,AdjList[maxn];

struct {
    AdjList vex; //定点表(链表的头)
    int vexnum, edgenum;//当前图的顶点数和边数
}AdjGraph;

最小生成树:

prime:

#include<iostream>
#include<vector>
#include<set>
using namespace std;
const int maxn = 100;

class MatGraph {
public:
    set<int> V;//顶点表
    int E[maxn][maxn];//边表
    int vn, en;//当前图的顶点数和边数
    MatGraph() {
        for (int i = 0; i < maxn; i++) {
            for (int j = 0; j < maxn; j++)
            {
                E[i][j] = -1;
            }
        }
        vn = en = 0;
    };

    void addV(int newV) {
        if (V.find(newV) == V.end()) {
            V.insert(newV);
            vn++;
        }
    };
    void addE(int begin, int end, int weight) {
        if (E[begin][end] == -1) {
            en++;
        }
        E[begin][end] = E[end][begin] = weight;
    };

};

const int max_weight = 0x3f3f3f;
void Prim(MatGraph G, MatGraph &T, int V) {//G为图,T为要返回的最小生成树,初始化为空, V为任取一个顶点
    T.addV(V);
    int vn = G.vn;
    while(T.vn != vn){//树中不含全部顶点
        int start = 0, end = 0, weight=max_weight;//权值最小的边
        for (auto v_st : T.V) {
            for (auto v_ed : G.V) {
                if (T.V.find(v_ed) == T.V.end() && G.E[v_st][v_ed]!= -1 && G.E[v_st][v_ed] < weight) {
                    start = v_st, end = v_ed, weight = G.E[v_st][v_ed];
                }
            }
        }
        T.addE(start, end, weight);
        T.addV(end);
    }
}

struct E{
    int start, end, weight;
};

int main()
{
    MatGraph G;
    for (int i = 1; i < 7; i++)
    {
        G.addV(i);
    }
    E e[10] = { {1,2,6},{1,4,5},{1,3,1},{2,3,5},{3,4,5},{2,5,3},{3,5,6},{3,6,4},{4,6,2},{5,6,6} };
    for (int i = 0; i < 10; i++)
    {
        G.addE(e[i].start, e[i].end, e[i].weight);
    }
    MatGraph T;
    Prim(G, T, 1);
    for (size_t i = 1; i <= T.vn; i++)
    {
        for (size_t j = 1; j <= T.vn; j++)
        {
            if (T.E[i][j] != -1)
                cout << i << " " << j << " " << T.E[i][j] << endl;
        }
    }
    return 0;
}

Kruskal:

void kruskal(MatGraph G, MatGraph& T) {
	for (auto v : G.V) {
		T.addV(v);
	}
	DisjointSet ds(T.vn);
	while (ds.count > 1) {
		int vs = 0, vd = 0, weight = max_weight;
		for (int i = 1; i <= T.vn; i++)
		{
			for (int j = 1; j <= T.vn; j++)
			{
				if (G.E[i][j] != -1 && ds.find(i)!= ds.find(j) && G.E[i][j] < weight) {
					vs = i, vd = j, weight = G.E[i][j];
				}
			}
		}
		T.addE(vs, vd, weight);
		ds.merge(vs, vd);
	}
}

最短路径:

Dijkstra:

void Dijkstra(MatGraph G, int vs, int vd) {
    int vn = G.vn;
    vector<int> dist(vn);
    vector<int> pathAhead(vn);
    pathAhead[vs] = vs;
    for (int i = 1; i <= G.vn; i++)
    {
        dist[i] = max_weight;
    }
    dist[vs] = 0;
    set<int> V;
    V.insert(vs);
    int cur_short = vs;
    while (V.size() != G.vn) {
        for (int i = 1; i <= G.vn; i++)
        {
            if (G.E[cur_short][i] != -1 && dist[cur_short] + G.E[cur_short][i] < dist[i]) {
                dist[i] = dist[cur_short] + G.E[cur_short][i];
                pathAhead[i] = cur_short;
            }
        }
        int weight = max_weight;
        for (int i = 1; i <= G.vn; i++)
        {
            if (V.find(i) == V.end() && dist[i] < weight) {
                weight = dist[i];
                cur_short = i;
            }
        }
        V.insert(cur_short);
    }

    //print inversed path from vs to vd :
    cur_short = vd;
    while (cur_short != vs) {
        cout << cur_short << endl;
        cur_short = pathAhead[cur_short];
    }
    cout << cur_short << endl;
}

Bellman-Ford:

const int max_weight = 0x3f3f3f3f;
void Bellman_Ford(MatGraph G, int vs) {
    int vn = G.vn;
    vector< vector<int> > dist(vn+1, vector<int>(vn+1, max_weight));
    dist[0][vs] = 0;
    for (int i = 1; i < vn; i++)
    {
        for (auto v : G.V){
            int minDist = max_weight;
            for (auto pv : G.V) {
                if (G.E[pv][v] != -1 && dist[i - 1][pv] + G.E[pv][v] < minDist)
                    minDist = dist[i - 1][pv] + G.E[pv][v];
            }
            dist[i][v] = min(dist[i - 1][v], minDist);
        }
    }

    //print all distances from vs to all vertex.
    for (auto v : G.V)
        cout << vs << " to " << v << ":" << dist[vn - 1][v] << endl;
}

Floyd_Warshall:

const int max_weight = 0x3f3f3f3f;
void Floyd_Warshall(MatGraph G) {
    int vn = G.vn;
    //初始化
    vector<vector<vector<int>>> dist(vn + 1, vector<vector<int>>(vn + 1, vector<int>(vn + 1, max_weight)));
    for (int i = 0; i <= vn; i++) {
        for (int j = 1; j <= vn; j++)
        {
            for (int k = 1; k <= vn; k++) {
                if (j == k)
                    dist[i][j][k] = 0;
                else if (G.E[j][k] != -1)
                    dist[i][j][k] = G.E[j][k];
            }
        }
    }
    //i作为中间节点,找到dist[i-1][j][i] + dist[i-1][i][k] < dist[i][j][k] 
    for (int i = 1; i <= vn; i++) {
        for (int j = 1; j <= vn; j++) {
            for (int k = 1; k <= vn; k++) {
                dist[i][j][k] = min(dist[i - 1][j][k], dist[i - 1][j][i] + dist[i - 1][i][k]);
            }
        }
    }

    for (int j = 1; j <= vn; j++) {
        for (int k = 1; k <= vn; k++) {
            cout << "shortest path from " << j << " to " << k << ":" << dist[vn][j][k] << endl;
        }
    }
}

拓扑排序:

广度优先搜索拓扑排序:

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define MAXV 10005
 
 
vector<int> vec[MAXV];
vector<int> ans;
 
int v, e;
 
int indeg[MAXV] = {0};
bool vis[MAXV] = {false};
 
void bfs(int u)
{
    queue<int>q;
    vis[u] = true;
    q.push(u);
 
    while(!q.empty())
    {
        int t = q.front();
        q.pop();
        ans.push_back(t);
        for(int x:vec[t])
        {
            indeg[x]--;
            if(indeg[x]==0&&vis[x]==false)
            {
                q.push(x);
                vis[x] = true;
 
            }
        }
    }
}
 
void solve()
{
    for (int i = 0; i < v; ++i)
    {
        if (indeg[i] == 0 && vis[i] == false)
        {
            bfs(i);
        }
    }
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    cin >> v >> e;
 
    int s, t;
    for (int i = 0; i < e; ++i)
    {
        cin >> s >> t;
        vec[s].push_back(t);
        indeg[t]++;
    }
    solve();
    for(int x:ans)
    {
        cout<<x<<endl;
    }
}

深度优先深度搜索:

vector<int>g[N];//邻接表存储
int vis[N],topo[N],cnt;
bool dfs(int u)
{
    vis[u] = -1;//-1用来表示顶点u正在访问
    for(int i = 0 ; i < g[u].size() ; i ++)
    {
        if(vis[g[u][i]] == -1)//表示这个点进入了两次,肯定出现了环
            return false;
        else if(vis[g[u][i]] == 0)
        {
            dfs(g[u][i]);
        }
    }
    vis[u] = 1;
    topo[cnt++] = u;//放到结果数组里,输出的时候记得倒序输出,(回溯的原因)
    return true;
}
bool toposort(int n)
{
    memset(vis,0,sizeof(vis));
    for(int i = 1 ; i <= n ; i ++)
    {
        if(!vis[i])
        {
            if(!dfs(i)) return false;//huan
        }
    }
    return true;
}
#include<iostream>

using namespace std;

int main(){
    int ans = 0;
    int n,m;
    cin>> n>>m;

    int l = 1;
    int j;
    cin>>j;
    int arr[10000];
    for(int l1=1; l1<=j; l1++){
        int tmp;
        cin>>tmp;
        arr[l1] = tmp;
    } 
    int cur=1;
    int next;
    while(cur<j){
        next = cur+1;
        if(arr[next] >= arr[cur]){
            int Min = min(abs(arr[cur] - l), abs(arr[next]-(l+m-1)));

            ans += Min;
            if(Min == abs(arr[cur]-l)){
                l=arr[cur];
            }
            else{
                l = arr[next]-m+1;
            }
            
            
            if(arr[next] <= l+m-1){
                cur +=2;
            }
            else{
                cur++;
            }
        }
        else{
            int Min = min(abs(arr[cur]-(l+m-1)), abs(arr[next]-l));
            ans += Min;
            if(Min = abs(arr[cur]-l+m-1))
                l = arr[cur]+1-m;
            else{
                l = arr[next];
            }
            if(arr[next] >= l){
                cur += 2;
            }
            else{
                cur++;
            }
        }
    }
    if(arr[cur] >= l && arr[cur] <= l+m-1){
    }
    else{
        ans += min(abs(l-arr[cur]), abs(l+m-1-arr[cur]));
    }
    cout << ans << endl;
}

文档信息

Search

    Table of Contents