ACM模板

数据类型的取值范围

char

-128 ~ 127 (1 Byte,大约3位)

short

-32768 ~ 32767 (2 Bytes,大约五位)

unsigned short

0 ~ 65536 (2 Bytes,大约五位)

int

-2147483648 ~ 2147483647 (4 Bytes,大约十位)

unsigned int

0 ~ 4294967295 (4 Bytes,大约十位)

long

扫描二维码关注公众号,回复: 102069 查看本文章

== int

long long

-9223372036854775808 ~ 9223372036854775807 (8 Bytes,大约十九位)

unsigned long long

0 ~ 18446744073709551615(大约二十位)

__int64

== long long

unsigned __int64

== unsigned long long

double

1.7 * 10^308 (8 Bytes)

 

using namespace std;

const int maxn = 110;

const int INF = 0x3f3f3f3f;

博弈论

Bash

#define _MAX 10000
int a[_MAX];
int b[_MAX];
 
int bash(int N, int K)
{
    if (N % (K + 1) == 0) 
    {
        return 2;
    }
    return 1;
}
 
int main()
{
    int T;
    scanf("%d", &T);
    for (int i = 0; i < T; i++)
    {
        scanf("%d%d", a + i, b + i);
    }
    for (int i = 0; i < T; i++)
    {
        if (bash(a[i], b[i]) == 1)
        {
            printf("A\n");
        }
        else
        {
            printf("B\n");
        }
    }
    return 0; 
}

Nim

int main(int argc, const char * argv[])
{
    int N, stone, tag = 0;
    scanf("%d", &N);
    while (N--)
    {
        scanf("%d", &stone);
        tag ^= stone;
    }
    //tag为0则为后手赢,否则为先手赢
    printf("%c\n", tag == 0 ? 'B' : 'A');
    return 0;
}

SG函数

SG打表

const int MAX_DIG = 64;
 
//  SG打表
//  f[]:可以取走的石子个数
//  sg[]:0~n的SG函数值
//  hash[]:mex{}
int f[MAX_DIG];
int sg[MAX_DIG];
int hash[MAX_DIG];
 
void getSG(int n)
{
    memset(sg, 0, sizeof(sg));
    for (int i = 1; i <= n; i++)
    {
        memset(hash, 0, sizeof(hash));
        for (int j = 1; f[j] <= i; j++)
        {
            hash[sg[i - f[j]]] = 1;
        }
        for (int j = 0; j <= n; j++)    //  求mes{}中未出现的最小的非负整数
        {
            if (hash[j] == 0)
            {
                sg[i] = j;
                break;
            }
        }
    }
}

SG DFS

const int MAX_DIG = 64;
 
//  DFS
//  注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//  n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[MAX_DIG];
int sg[MAX_DIG * 100];
int n;
 
int SG_dfs(int x)
{
    if (sg[x] != -1)
    {
        return sg[x];
    }
    bool vis[MAX_DIG];
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < n; i++)
    {
        if (x >= s[i])
        {
            SG_dfs(x - s[i]);
            vis[sg[x - s[i]]] = 1;
        }
    }
    int e;
    for (int i = 0; ; i++)
    {
        if (!vis[i])
        {
            e = i;
            break;
        }
    }
    return sg[x] = e;
}

Wythoff有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品

int main()
{
    int t, a, b, m, k;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &a, &b);
        if (a > b)
        {
            a ^= b;
            b ^= a;
            a ^= b;
        }
        m = b - a;
        k = (int)(m * (1 + sqrt(5)) / 2.0);
        //m = ? * a
        //k = m / ?
        //?:黄金分割数
        //如果a == k,则为后手赢,否则先手赢(奇异局)
        printf("%s\n", a == k ? "B" : "A");
    }
    return 0;
}

经典

1.埃拉托斯特尼筛法/*|埃式筛法||快速筛选素数|*/

int prime[maxn]; 

bool is_prime[maxn];

 

int sieve(int n){

    int p = 0;

    for(int i = 0;i <= n; ++i)

        is_prime[i]= true;

    is_prime[0] =is_prime[1] = false;

    for (int i = 2;i <= n; ++i){   //  注意数组大小是n

        if(is_prime[i]){

           prime[p++] = i;

            for(intj = i + i; j <= n; j += i)  //  轻剪枝,j必定是i的倍数

               is_prime[j] = false;

        }

    }

    return p;   //  返回素数个数

}

2.快速幂

typedef long long LL;   //  视数据大小的情况而定

 

LL powerMod(LL x, LL n, LL m)

{

    LL res = 1;

    while (n >0){

        if  (n & 1) //  判断是否为奇数,若是则true

            res =(res * x) % m;

        x = (x * x)% m;

        n >>=1;    // 相当于n /= 2;

    }

    return res;

}

3.大数模拟

大数加法

string add1(string s1, string s2)

{

    if (s1 =="" && s2 == "")  return "0";

    if (s1 =="")   return s2;

    if (s2 =="")   return s1;

    string maxx =s1, minn = s2;

    if (s1.length()< s2.length()){

        maxx = s2;

        minn = s1;

    }

    int a =maxx.length() - 1, b = minn.length() - 1;

    for (int i = b;i >= 0; --i){

        maxx[a--]+= minn[i] - '0'; //  a一直在减 , 额外还要减个'0'

    }

    for (int i =maxx.length()-1; i > 0;--i){

        if (maxx[i]> '9'){

            maxx[i]-= 10;//注意这个是减10

            maxx[i- 1]++;

        }

    }

    if (maxx[0]> '9'){

        maxx[0] -=10;

        maxx = '1'+ maxx;

    }

    return maxx;

}

大数阶乘

#include <iostream>

#include <cstdio>

 

using namespace std;

 

typedef long long LL;

 

const int maxn = 100010;

 

int num[maxn], len;

 

/*

    在mult函数中,形参部分:len每次调用函数都会发生改变,n表示每次要乘以的数,最终返回的是结果的长度

    tip: 阶乘都是先求之前的(n-1)!来求n!

    初始化Init函数很重要,不要落下

*/

 

void Init() {

    len = 1;

    num[0] = 1;

}

 

int mult(int num[], int len, int n) {

    LL tmp = 0;

    for(LL i = 0; i< len; ++i) {

         tmp = tmp+ num[i] * n;    //从最低位开始,等号左边的tmp表示当前位,右边的tmp表示进位(之前进的位)

         num[i] =tmp % 10; //  保存在对应的数组位置,即去掉进位后的一位数

         tmp = tmp/ 10;    //  取整用于再次循环,与n和下一个位置的乘积相加

    }

    while(tmp){    // 之后的进位处理

         num[len++]= tmp % 10;

         tmp = tmp/ 10;

    }

    return len;

}

 

int main() {

    Init();

    int n;

    n = 1977; // 求的阶乘数

    for(int i = 2;i <= n; ++i) {

        len =mult(num, len, i);

    }

    for(int i = len- 1; i >= 0; --i)

       printf("%d",num[i]);   //  从最高位依次输出,数据比较多采用printf输出

   printf("\n");

    return 0;

}

4.GCD最大公约数 |辗转相除法||欧几里得算法|

int gcd(int big, int small)

{

    if (small >big) swap(big, small);

    int temp;

    while (small !=0){ //  辗转相除法

        if (small> big) swap(big, small);

        temp = big% small;

        big =small;

        small =temp;

    }

    return(big);

}

5.LCM最小公倍数 |辗转相除法||欧几里得算法|

int gcd(int big, int small)

{

    if (small >big) swap(big, small);

    int temp;

    while (small !=0){ //  辗转相除法

        if (small> big) swap(big, small);

        temp = big% small;

        big =small;

        small =temp;

    }

    return(big);

}

6.全排列|求1到n的全排列, 有条件|

void Pern(int list[], int k, int n) {   //  k表示前k个数不动仅移动后面n-k位数

    if (k == n - 1){

        for (int i= 0; i < n; i++) {

           printf("%d", list[i]);

        }

       printf("\n");

    }else {

        for (int i= k; i < n; i++) {   //  输出的是满足移动条件所有全排列

           swap(list[k], list[i]);

           Pern(list, k + 1, n);

           swap(list[k], list[i]);

        }

    }

}

7.二分搜索 |二分搜索||要求:先排序|

//  left为最开始元素, right是末尾元素的下一个数,x是要找的数

int bsearch(int *A, int left, int right, int x){

    int m;

    while (left< right){

        m = left +(right - left) / 2;

        if (A[m]>= x)  right = m;   else left = m + 1;   

        // 如果要替换为 upper_bound, 改为:if (A[m]<= v) x = m+1; else y = m;    

    }

    return left;

}

 

/*

    最后left == right 

 

    如果没有找到135577找6,返回7 

 

    如果找有多少的x,可以用lower_bound查找一遍,upper_bound查找一遍,下标相减 

 

    C++自带的lower_bound(a,a+n,x)返回数组中最后一个x的下一个数的地址

 

   upper_bound(a,a+n,x)返回数组中第一个x的地址

 

    如果a+n内没有找到x或x的下一个地址,返回a+n的地址 

 

   lower_bound(a,a+n,x)-upper_bound(a,a+n,x)返回数组中x的个数

*/

数据结构

并查集

8.并查集 |合并节点操作|

int father[maxn];  //  储存i的father父节点 

 

void makeSet() { 

    for (int i = 0;i < maxn; i++)  

        father[i] =i; 

 

int findRoot(int x) {  //  迭代找根节点

    int root = x;// 根节点 

    while (root !=father[root]) { // 寻找根节点 

        root =father[root]; 

    } 

    while (x !=root) { 

        int tmp =father[x]; 

        father[x] =root; // 根节点赋值 

        x =tmp; 

    } 

    returnroot; 

 

void Union(int x, int y) {  //  将x所在的集合和y所在的集合整合起来形成一个集合。 

    int a, b; 

    a =findRoot(x); 

    b =findRoot(y); 

    father[a] =b;  // y连在x的根节点上  或father[b] = a为x连在y的根节点上; 

 

/*

    在findRoot(x)中:

    路径压缩 迭代 最优版

    关键在于在路径上的每个节点都可以直接连接到根上

*/

图论

MST

最小生成树

Kruskal

9.克鲁斯卡尔算法|Kruskal算法|

    |适用于 稀疏图 求最小生成树|

 

/*

    第一步:点、边、加入vector,把所有边按从小到大排序

    第二步:并查集部分 + 下面的code

*/

 

void Kruskal() {   

    ans = 0;   

    for (int i = 0;i<len; i++) {   

        if(Find(edge[i].a) != Find(edge[i].b)) {   

           Union(edge[i].a, edge[i].b);   

            ans +=edge[i].len;   

        }   

    }   

}   

Prim

10.普里姆算法    |Prim算法||适用于稠密图 求最小生成树||堆优化版,时间复杂度:O(elgn)|

struct node { 

    int v,len; 

    node(int v = 0,int len = 0) :v(v), len(len) {} 

    bool operator< (const node &a)const {  // 加入队列的元素自动按距离从小到大排序 

        returnlen> a.len; 

    } 

};

 

vector<node> G[maxn];

int vis[maxn];

int dis[maxn];

 

void init() { 

    for (int i = 0;i<maxn; i++) { 

       G[i].clear(); 

        dis[i] = INF; 

        vis[i] =false; 

    } 

int Prim(int s) { 

   priority_queue<node>Q; // 定义优先队列 

    int ans =0; 

   Q.push(node(s,0));  // 起点加入队列 

    while(!Q.empty()) {  

        node now =Q.top(); Q.pop();  // 取出距离最小的点 

        int v = now.v; 

        if (vis[v])continue;  // 同一个节点,可能会推入2次或2次以上队列,这样第一个被标记后,剩下的需要直接跳过。 

        vis[v] =true;  // 标记一下 

        ans +=now.len; 

        for (int i= 0; i<G[v].size(); i++) {  // 开始更新 

            int v2= G[v][i].v; 

            int len = G[v][i].len; 

            if(!vis[v2] && dis[v2] > len) {  

               dis[v2] = len; 

               Q.push(node(v2, dis[v2]));  // 更新的点加入队列并排序 

            } 

        } 

    } 

    return ans;

Bellman-Ford

单源最短路

Dijkstra

11.迪杰斯特拉算 |适用于边权为正的有向图或者无向图||求从单个源点出发,到所有节点的最短路|

struct node { 

    int v,len; 

    node(int v = 0,int len = 0) :v(v), len(len) {} 

    bool operator< (const node &a)const {  //  距离从小到大排序 

        return len> a.len; 

    } 

}; 

 

vector<node>G[maxn]; 

bool vis[maxn]; 

int dis[maxn];

 

void init() { 

    for (int i = 0;i<maxn; i++) { 

       G[i].clear(); 

        vis[i] =false; 

        dis[i] =INF; 

    } 

int dijkstra(int s, int e) { 

   priority_queue<node>Q; 

    Q.push(node(s,0)); //  加入队列并排序 

    dis[s] =0; 

    while(!Q.empty()) { 

        node now =Q.top();     //  取出当前最小的 

       Q.pop(); 

        int v =now.v; 

        if (vis[v])continue;   //  如果标记过了, 直接continue 

        vis[v] =true; 

        for (int i = 0; i<G[v].size(); i++){   // 更新 

            int v2= G[v][i].v; 

            int len= G[v][i].len; 

            if(!vis[v2] && dis[v2] > dis[v] + len) { 

               dis[v2] = dis[v] + len; 

               Q.push(node(v2, dis[v2])); 

            } 

        } 

    } 

    returndis[e]; 

SPFA

12.最短路径快速算法(Shortest Path FasterAlgorithm)|SPFA算法||队列优化| |可处理负环|

vector<node> G[maxn];

bool inqueue[maxn];

int dist[maxn];

 

void Init() 

    for(int i = 0 ;i < maxn ; ++i){ 

       G[i].clear(); 

        dist[i] =INF; 

    } 

int SPFA(int s,int e) 

    intv1,v2,weight; 

   queue<int> Q; 

   memset(inqueue,false,sizeof(inqueue)); // 标记是否在队列中 

   memset(cnt,0,sizeof(cnt)); // 加入队列的次数 

    dist[s] = 0; 

    Q.push(s); // 起点加入队列 

    inqueue[s] =true; // 标记 

   while(!Q.empty()){ 

        v1 =Q.front(); 

       Q.pop(); 

        inqueue[v1]= false; // 取消标记 

        for(int i =0 ; i < G[v1].size() ; ++i){ // 搜索v1的链表 

            v2 =G[v1][i].vex; 

            weight= G[v1][i].weight; 

           if(dist[v2] > dist[v1] + weight){ // 松弛操作 

               dist[v2] = dist[v1] + weight; 

               if(inqueue[v2] == false){  // 再次加入队列 

                   inqueue[v2] = true; 

                    //cnt[v2]++;  // 判负环 

                   //if(cnt[v2] > n) return -1; 

                   Q.push(v2); 

                } }} 

    } 

    returndist[e]; 

}

 

/*

    不断的将s的邻接点加入队列,取出不断的进行松弛操作,直到队列为空 

 

    如果一个结点被加入队列超过n-1次,那么显然图中有负环 

*/

Floyd-Warshall

13.弗洛伊德算法

|Floyd算法| |任意点对最短路算法| |求图中任意两点的最短距离的算法|

for (int i = 0; i < n; i++) {   //  初始化为0 

    for (int j = 0;j < n; j++) 

       scanf("%lf", &dis[i][j]); 

for (int k = 0; k < n; k++) { 

    for (int i = 0;i < n; i++) { 

        for (int j = 0; j < n; j++) { 

           dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); 

        } 

    }

}

二分图

14.染色法|交叉染色法判断二分图|

int bipartite(int s) { 

    int u, v; 

   queue<int>Q; 

    color[s] =1; 

    Q.push(s); 

    while (!Q.empty()){ 

        u =Q.front(); 

       Q.pop(); 

        for (int i= 0; i < G[u].size(); i++) { 

            v =G[u][i]; 

            if(color[v] == 0) { 

               color[v] = -color[u]; 

               Q.push(v); 

            } 

            else if (color[v] == color[u]) 

               return 0; 

        } 

    } 

    return 1; 

15..匈牙利算法 |求解最大匹配问题||递归实现|

vector<int>G[maxn]; 

bool inpath[maxn]; //  标记 

int match[maxn];   //  记录匹配对象 

void init() 

    memset(match,-1, sizeof(match)); 

    for (int i = 0;i < maxn; ++i) { 

       G[i].clear(); 

    } 

bool findpath(int k) { 

    for (int i = 0;i < G[k].size(); ++i) { 

        int v =G[k][i]; 

        if(!inpath[v]) { 

           inpath[v] = true;  

            if(match[v] == -1 || findpath(match[v])) { // 递归 

               match[v] = k; // 即匹配对象是“k妹子”的 

               return true; 

            } 

        } 

    } 

    returnfalse; 

 

void hungary() { 

    int cnt =0; 

    for (int i = 1;i <= m; i++) {  // m为需要匹配的“妹子”数 

       memset(inpath, false, sizeof(inpath)); // 每次都要初始化 

        if(findpath(i)) cnt++; 

    } 

    cout <<cnt << endl; 

/*|求解最大匹配问题| |dfs实现|*/

int v1, v2; 

bool Map[501][501]; 

bool visit[501]; 

int link[501]; 

int result; 

 

bool dfs(int x) { 

    for (int y = 1;y <= v2; ++y)  { 

        if(Map[x][y] && !visit[y])  { 

           visit[y] = true; 

            if(link[y] == 0 || dfs(link[y]))  { 

               link[y] = x; 

                return true; 

            } }} 

    returnfalse; 

 

 

void Search() { 

    for (int x = 1;x <= v1; x++)  { 

       memset(visit,false,sizeof(visit)); 

        if(dfs(x)) 

           result++; 

    }

}

动态规划

背包

16.17.18背包问题

//  01背包: 

void bag01(int cost,int weight)  { 

    for(i = v; i>= cost; --i) 

    dp[i] =max(dp[i], dp[i-cost]+weight); 

 

//  完全背包: 

void complete(int cost, int weight)  { 

    for(i = cost ;i <= v; ++i) 

    dp[i] =max(dp[i], dp[i - cost] + weight); 

}  

 

//  多重背包: 

 

void multiply(int cost, int weight, int amount)  { 

    if(cost *amount >= v) 

       complete(cost, weight); 

    else{ 

        k = 1; 

        while (k< amount){ 

            bag01(k* cost, k * weight); 

            amount-= k; 

            k +=k; 

        } 

        bag01(cost* amount, weight * amount); 

    } 

 

// other

int dp[1000000];

int c[55], m[110];

int sum;

 

void CompletePack(int c) {

    for (int v = c;v <= sum / 2; ++v){

        dp[v] =max(dp[v], dp[v - c] + c);

    }

}

 

void ZeroOnePack(int c) {

    for (int v =sum / 2; v >= c; --v) {

        dp[v] =max(dp[v], dp[v - c] + c);

    }

}

 

void multiplePack(int c, int m) {

    if (m * c >sum / 2)

       CompletePack(c);

    else{

        int k = 1;

        while (k < m){

           ZeroOnePack(k * c);

            m -= k;

            k<<= 1;

        }

        if (m !=0){

           ZeroOnePack(m * c);

        }

    }

}

19.最长上升子序列    |最长上升子序列||状态转移|/*

    状态转移dp[i] = max{ 1.dp[j] + 1 };  j<i; a[j]<a[i];

    d[i]是以i结尾的最长上升子序列

    与i之前的 每个a[j]<a[i]的 j的位置的最长上升子序列+1后的值比较

*/

 

void solve(){   // 参考挑战程序设计入门经典;

    for(int i = 0;i < n; ++i){ 

        dp[i] =1; 

        for(int j =0; j < i; ++j){ 

            if(a[j]< a[i]){ 

               dp[i] = max(dp[i], dp[j] + 1); 

            } } }

 

/*

    优化方法:

    dp[i]表示长度为i+1的上升子序列的最末尾元素 

    找到第一个比dp末尾大的来代替

*/

 

    void solve(){ 

        for (int i= 0; i < n; ++i){

            dp[i] =INF;

        }

        for (int i= 0; i < n; ++i) { 

            *lower_bound(dp, dp + n, a[i]) = a[i];  //  返回一个指针 

        } 

       printf("%d\n", *lower_bound(dp, dp + n, INF) - dp; 

    }

 

/* 

    函数lower_bound()返回一个 iterator 它指向在[first,last)标记的有序序列中可以插入value,而不会破坏容器顺序的第一个位置,而这个位置标记了一个不小于value的值。

*/

LCS

20.最长公共子序列 |求最长公共子序列||递推形式|

void solve() { 

    for (int i = 0;i < n; ++i) { 

        for (int j= 0; j < m; ++j) { 

            if(s1[i] == s2[j]) { 

               dp[i + 1][j + 1] = dp[i][j] + 1; 

            }else{ 

               dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]); 

            } } }

计算几何

22.求多边形面积

node G[maxn]; 

int n; 

 

double Cross(node a, node b) { // 叉积计算 

    return a.x*b.y- a.y*b.x; 

 

 

int main() 

    while(scanf("%d", &n) != EOF && n) { 

        for (int i= 0; i < n; i++)  

           scanf("%lf %lf", &G[i].x, &G[i].y); 

        double sum= 0; 

        G[n].x =G[0].x; 

        G[n].y =G[0].y; 

        for (int i= 0; i < n; i++) {  

                sum+= Cross(G[i], G[i + 1]); 

        }  

        // 或者 

            //for(int i = 0; i < n; i++) { 

               //sum += fun(G[i], G[(i + 1)% n]); 

           //} 

        sum = sum /2.0; 

       printf("%.1f\n", sum); 

    } 

   system("pause"); 

    return 0; 

}

23..判断线段相交

/*

    |16/11/06ztx|

*/

 

node P[35][105];    

 

double Cross_Prouct(node A,node B,node C) {     //  计算BA叉乘CA    

    return(B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);     

}     

bool Intersect(node A,node B,node C,node D)  { //  通过叉乘判断线段是否相交;          

   if(min(A.x,B.x)<=max(C.x,D.x)&&         // 快速排斥实验;     

      min(C.x,D.x)<=max(A.x,B.x)&&     

      min(A.y,B.y)<=max(C.y,D.y)&&     

      min(C.y,D.y)<=max(A.y,B.y)&&     

      Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&&      // 跨立实验;      

      Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0)       // 叉乘异号表示在两侧;     

       returntrue;     

    else returnfalse;     

}   

24.求三角形外心

Point circumcenter(const Point &a, const Point&b, const Point &c) { //返回三角形的外心       

    Point ret; 

    double a1 = b.x- a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1) / 2; 

    double a2 = c.x- a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2) / 2; 

    double d =a1*b2 - a2*b1; 

    ret.x = a.x +(c1*b2 - c2*b1) / d; 

    ret.y = a.y +(a1*c2 - a2*c1) / d; 

    returnret; 

猜你喜欢

转载自blog.csdn.net/qq_36761831/article/details/80172265