算法板子

0、一般算法

二维前缀和

#include<bits/stdc++.h>
using namespace std;
const int num = 6;
int mapp[num][num] , sum[num][num];
int main(){
    int x1,y1,x2,y2;
    for(int i = 1; i < num; i ++)
        for(int j = 1; j < num; j ++)
            mapp[i][j] = 1 + rand() % 5;
    for(int i = 1; i < num; i ++)
        for(int j = 1; j < num; j ++)
            sum[i][j] = mapp[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
    while(cin >> x1 >> y1 >> x2 >> y2)
    {
        cout << sum[x2][y2] - sum[x1][y1] << endl;
    }
}  

1、 图论

跳转到目录

最小生成树

Kruskal算法并查集实现

//板子适配题目洛谷:R18683676
#include<bits/stdc++.h>
using namespace std;
const int MN = 5000 + 5;
const int ME = 200000 + 5;
struct node
{
   int u , v , w;
   bool operator < (const node &b)const{
   	return w < b.w;
   }
};
vector<node> eage;
int pa[MN] = {0};
int find(int x){
   return (pa[x] == x ? x : find(pa[x]));
}
int main(){
   int n , e;
   cin >> n >> e;
   for(int i = 0; i < e; i ++)
   {
   	int u , v , w;
   	cin >> u >> v >> w;
   	eage.push_back((node){u,v,w});
   }
   sort(eage.begin() , eage.end());
   for(int i = 0; i < MN; i ++)
   {
   	pa[i] = i;
   }
   int cnt = 0 , ans = 0;
   for(int i = 0; i < e; i ++){
   	int u = eage[i].u;
   	int v = eage[i].v;
   	int w = eage[i].w;
   	u = find(u);
   	v = find(v);
   	if(u == v) continue;
   	pa[u] = v;
   	ans += w;
   	cnt ++;
   	if(cnt >= n - 1) break;
   }
   cout << ans;
}

单源最短路径

使用Dijkstra算法和优先队列优化

#include<bits/stdc++.h>
using namespace std;
const int MN = 100000 + 5;
const int ME = 200000 + 5;
const int INF = 2147483647;
struct node{
    int v , w;
    bool operator < (const node &b)const{
        return w > b.w;
    }
};
vector<node> eage[MN];
priority_queue<node> q;
int book[MN];
int dis[MN];
int main(){
    int n , e , s;
    cin >> n >> e >> s;
    for(int i = 0; i < e; i ++){
        int u , v , w;
        cin >> u >> v >>w;
        eage[u].push_back((node){v,w});
    }
    //存入所有边
    for(int i = 1; i <= n; i ++) dis[i] = INF;
    dis[s] = 0;
    q.push((node){s,0});
    while(!q.empty()){
        node cur = q.top();q.pop();
        int u = cur.v;
        if(book[u]) continue;
        book[u] = 1;
        for(int i = 0; i < eage[u].size(); i ++){
            int v = eage[u][i].v;
            int cost = eage[u][i].w;
            if(!book[v] && dis[v] > dis[u] + cost){
            //book[]数组标记了在一次更新后,某个点到原点的最短路径
                dis[v] = dis[u] + cost;
                q.push((node){v,dis[v]});
            }
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        cout << dis[i] << " ";
    }
}

简单粗暴的Bellman Ford算法

#include<bits/stdc++.h>
using namespace std;
const long long MN = 10005;
const long long MM = 500005;
struct node
{
	long long u , v , w;
};
vector<node> eage;
long long dis[MN];
int Bellman_ford(){
	long long n , m , s;
	int flag = 0;
	cin >> n >> m >> s;
	for(long long i = 0; i < m; i ++){
		long long u , v , w;
		cin >> u >> v >> w;
		eage.push_back((node){u,v,w});
	}
	for(int i = 0; i < MN; i ++){
		dis[i] = 2147483647;
	}
	dis[s] = 0;
	for(long long times = 1; times <= n - 1; times ++)
	{
		for(long long i = 0; i < eage.size(); i ++)
		{
			long long u = eage[i].u;
			long long v = eage[i].v;
			long long w = eage[i].w;
			if(dis[v] > dis[u] + w)
			{
				dis[v] = dis[u] + w;
				flag = 1;
			}
		}
	}
	if(flag == 0) return 0;
	for(long long i = 0; i < eage.size(); i ++)
		{
			long long u = eage[i].u;
			long long v = eage[i].v;
			long long w = eage[i].w;
			if(dis[v] > dis[u] + w)
			{
				return 1;
			}
		}
	return 0;
}
int main(){
	int ans = Bellman_ford();
	cout << "Have negative circle ? ";
	cout << ans;
}
/*
简单样例1:
5 7 1
1 2 100
1 3 30
1 5 10
3 2 60
3 4 60
4 2 10
5 4 50
没有负环
简单样例2:
4 6 1
1 2 20
1 3 5
4 1 -200
2 4 4
4 2 4
3 4 2
有负环
*/

BF的队列优化SPFA

#include<bits/stdc++.h>
using namespace std;
const int MN = 100000 + 5;
const int NE = 200000 + 5;
struct node
{
	int v , w;
};
int book[MN];//判断节点是否在队列当中
queue<int> q;//进队进行松弛操作
int dis[MN];
vector<node> eage[MN];
int cnt[MN]; // 统计每个节点进队次数.如果某点大于节点数,则有负权环
int main(){
	int n , e , s = 1;
	cin >> n >> e;
	for(int i = 0; i < e; i ++)
	{
		int u , v , w;
		cin >> u >> v >> w;
		eage[u].push_back((node){v,w});
	}
	memset(book , 0 , sizeof(book));
	memset(dis , 0x3f , sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s] = 0;
	q.push(s);
	cnt[s] ++;
	while(!q.empty()){
		int u = q.front();q.pop();
		book[u] = 0;
		for(int i = 0; i < eage[u].size(); i ++){
			int v = eage[u][i].v;
			int w = eage[u][i].w;
			if(dis[v] > dis[u] + w)
			{
				dis[v] = dis[u] + w;
				if(!book[v]){
					book[v] = 1;
					q.push(v);
					cnt[v] ++;
					if(cnt[v] > n) return 0;
				}
			}
		}
	}
	for(int i = 1; i <= n; i ++){
		cout << dis[i] << " ";
	}
}

并查集

#include<bits/stdc++.h>
using namespace std;
const int MN = 10005;
const int MM = 200005;
int pa[MN];
int find(int x){
    if(pa[x] == x) return x;
    else 
        return pa[x] = find(pa[x]);
}
int main(){
    int n , m;
    cin >> n >> m;
    for(int i = 0; i < MN; i ++){
        pa[i] = i;
    }
    while(m --){
        int chose , u , v;
        cin >> chose >> u >> v;
        if(chose == 1){
            u = find(u);
            v = find(v);
            pa[u] = v;
        }
        else{
            u = find(u);
            v = find(v);
            if(u == v)
                cout << "Y" << endl;
            else
                cout << "N" << endl;
        }
    }
}

2、数论

跳转到目录

大数运算

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

class MyApplication{
	public static void main(String[] args) {
			Scanner scan = new Scanner(System.in);
			BigDecimal b1 = scan.nextBigDecimal();
			BigDecimal b2 = scan.nextBigDecimal();
			System.out.println(b1.add(b2));     // 加
			System.out.println(b1.subtract(b2));// 减
			System.out.println(b1.multiply(b2));// 乘
			System.out.println(b1.divide(b2));  // 除
			BigInteger a = scan.nextBigInteger();
			BigInteger b = scan.nextBigInteger();
			System.out.println(a.gcd(b));       // gcd
			System.out.println(a.remainder(b)); // 取模
			scan.close();
	}
}

素数筛

#include<bits/stdc++.h>
using namespace std;
const int M = 100005;
int a[M];
int main(){
    for(int i = 2; i < M / 2; i ++){
        if(a[i]) continue;
        for(int j = 2; i * j < M; j ++){
            if(i * j < M) a[i * j] = 1;
        }
    }
}

快速幂

例如: a11=a(20+21+23)

long long ksm(long long a , long long b , long long mod){
	long long ans = 1 , base  = a;
	while(b)
	{
		if(b&1)
			ans = ans * base % mod;
		base = base * base % mod;
		//不推荐使用复合运算符
		b >>= 1;
	}
	return ans;
}

求逆元(费马小定理)

题外话:
同余方程:
a ≡ b ( m o d &ThinSpace; m ) a\equiv b (mod\,m) ab(modm)
a,b对于模m同余

关于取余的一些性质:
1. (a + b) % c = (a % c + b % c) % c
2. (a -  b) % c = (a % c - b % c + c) % c
3. (ab) % c = (a % c)(b % c) % c

值得注意的是在除运算中 :

a b % c = a % c b % c % c \frac{a}{b} \%c = \frac{a\%c}{b\%c}\% c ba%c=b%ca%c%c 正确吗?
答案很明显,是不对的.
但是我们能够把b转化成逆元 b − 1 b^{-1} b1 简化计算
即: b b − 1 ≡ 1 ( m o d c ) bb^{-1} \equiv 1(mod c) bb11(modc).

原式就转化为
a b % c → a b − 1 % c \frac{a}{b} \% c \rightarrow ab^{-1} \% c ba%cab1%c
这里我们需要使用费马小定理:
b c − 1 ≡ 1 ( m o d &ThinSpace; c ) b^{c - 1} \equiv 1(mod\,c) bc11(modc)(c是素数)
变式为:
b b c − 2 ≡ 1 ( m o d &ThinSpace; c ) bb^{c-2} \equiv 1(mod\,c) bbc21(modc)
也就是说, b c − 2 b^{c-2} bc2就是取余逆元.

int inv1 = ksm(b , mod - 2 , mod);
inv1 为b的逆元

辗转相除法求最大公约数和最小公倍数

typedef long long ll;
//求最大公约数
ll gcd(ll a , ll b)
{
	return b ? gcd(b,a % b) : a;
}
//求最小公倍数
ll lcm(ll a , ll b)
{
	return a * b / gcd(a,b);
}

贝祖公式

性质:
a x + b y = c ax+by = c ax+by=c 此类等式成立的充要条件为c是gcd(a,b)的整数倍
推广:
a x + b y + c z . . . . . + n m = f ax + by + cz ..... + nm = f ax+by+cz.....+nm=f 则f是gcd(a,b,c…n)的整数倍
公式证明(转载)
模板题

//多个数gcd的方法:
int n , ans = 0;
long long num;
cin >> n;
for(int i = 0; i < n; i ++)
{
    cin >> num;
    num = abs(num);
    if(!num) continue;
    ans = gcd(num,ans);
}
cout << ans;

拓展欧几里得算法

看了一位大佬的文章
欧几里得算法/扩展欧几里得算法
应用:
( 1 ) (1) (1)求二元一次方程的通解
( 2 ) (2) (2)求乘法逆元

拓展欧几里得大致推导:
欧几里得等式:gcd(a,b) = gcd(b,a%b)
我们使用迭代进行计算,设当前等式为:

  • a x 1 + b y 2 = g c d ( a , b ) ax_1 + by_2 = gcd(a,b) ax1+by2=gcd(a,b) -----(1)

则下一层等式为:

  • b x 2 + ( a % b ) y 2 = g c d ( b , a % b ) bx_2 + (a \% b)y_2 = gcd(b,a\%b) bx2+(a%b)y2=gcd(b,a%b) -----(2)

其中: ( a % b ) = a − a / b ∗ b (a\%b) = a- a / b * b (a%b)=aa/bb

根据欧几里得等式下一步推导得到:
a x 1 + b y 2 = b x 2 + ( a − a / b ∗ b ) y 2 ax_1 + by_2 = bx_2 + (a- a / b * b)y_2 ax1+by2=bx2+(aa/bb)y2
整理后得到:
a x 1 + b y 1 = a y 2 + b ( x 2 − [ a / b ] ∗ y 2 ax_1+ by_1 = ay_2 + b(x_2- [a / b] *y_2 ax1+by1=ay2+bx2[a/b]y2
通过上式,不难看出: x 1 = y 2 x_1 = y_2 x1=y2 , y 1 = x 2 − [ a / b ] ∗ y 2 y_1 = x_2 - [a / b] * y_2 y1=x2[a/b]y2
到目前为止,我们得到了一个类似状态转移方程的东西,接下来只要代码实现就行:

int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
    //最后一次迭代,联系到gcd可以明白
        x = 1;y = 0;
        return a;
    }
    int r = exgcd(b,a%b,x,y);
    int t = x; x = y;
    y = t-a/b*y;
    //已经由公式推导
    return r;
}

接下来,我们解析第二个应用:计算逆元 , 上面我们有提到通过费马小定理求解,但是费马小定理需要m为质数才有效果.而拓展欧几里得则没有这样的条件.

我们将 a x ≡ 1 ( m o d &ThinSpace; m ) ax \equiv 1(mod\,m) ax1(modm)中的 x x x称为a的逆元,即ax取m的余数是1 -----(1)

带余除法的定义为: a = y m + r ( m 为 商 , r 为 余 ) a = ym + r (m为商 , r 为余) a=ym+r(m,r)----- (2)

(2)式如下替换

r → 1 r\rightarrow1 r1 , a → a x \rightarrow ax ax

则有 a − y m = 1 a - ym = 1 aym=1

我们便得到ax - ym = 1这个二次等式 , 将(-y) 替换成 y 则有

  • a x + y m = 1 ax + ym = 1 ax+ym=1

这样,我们就能够通过欧几里得拓展求解了
以下是代码实现:

int mod_reverse(int a,int b)//a*x=1(mod b) 求a的逆元x 
{
    int d,x,y;
    d=exgcd(a,b,x,y);
    if(d==1)
        return (x%b+b)%b;
        //x可能是负数即不满足最小正整数的 , 通过+b再取模修正
    else
        return -1;
}

杨辉三角

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin >> n;
	int arr[100];
	for(int i = 1; i <= n; i ++)
	{
		int sum = 1;
		for(int j = 1; j <= i; j ++)
		{
			printf("%d\t",sum);
			sum = sum * (i - j) / j;
		}
		cout << endl;
	}
}

3、字符串处理

跳转到目录

KMP算法

#include<bits/stdc++.h>
using namespace std;
string str , pat;
int len1 , len2 , next[100];
void getnext(){
    memset(next , 0 , sizeof(next));
    int i = 1  , j = 0;
    while(i < len2)
    {
        if(pat[i] == pat[j])
        {
            next[i] = j + 1;
            i ++; j ++;
        }
        else
        {
            if(j == 0)
            i ++ && j ++;
            else
                j = next[j];
        }
    }
}
int find()
{
    int i = 0 , j = 0;
    while(i < len1 && j < len2)
    {
        if(str[i] == pat[j])
            i ++ && j ++;
        else
        {
            j = next[j] && i ++;
            i ++;
        }
    }
    if(j != len2)
        return i - len2;
    else
        return -1;
}
int main(){
    cin >> str >> pat;
    len1 = str.length() , len2 = pat.length();
    getnext();
    int index = find();
    cout << index;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值