Acwing-算法基础课

NOTE: 主要是学习期间的代码整理,y总算法基础课

KMP

code

#include<iostream>
using namespace std;

const int N = 10010, M = 100010;

int n, m;
char p[N], s[M];
int ne[N];

int main()
{
	cin >> n >> p + 1 >> m >> s + 1;
	
	// 求 next 
	for(int i = 2, j = 0; i <= n; i++){
		while(j && p[i] != p[j + 1]) j = ne[j];
		if(p[i] == p[j + 1]) j ++;
		ne[i] = j;
	}
	
	// kmp 匹配过程
	for(int i = 1, j = 0; i <= m; i++){
		while(j && s[i] != p[j + 1]){
			j = ne[j];
		}
		if(s[i] == p[j + 1]) j++;
		if(j == n){
			// success
			cout << i - n << " ";
			j = ne[j];
		}
	}
	return 0;
}

搜索与图论 — 求最短路径的问题

朴素Dijkstra算法

思想

code

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510;

// 朴素 Dijkstra 迪杰斯特拉算法 O(n ^ 2) 邻接矩阵 稠密图 n m 相差很大

int n, m;
int g[N][N];
int dist[N];
bool st[N];

int dijkstra()
{
	memset(dist, 0x3f, sizeof dist);
	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;
			}
		}
		st[t] = true;
		if(t == n) break;
	
		for(int j = 1; j <= n; j++){
			dist[j] = min(dist[j], dist[t] + g[t][j]);
		}
	}
	
	if(dist[n] == 0x3f3f3f3f) return -1;
	else return dist[n];
}

int main()
{
	
	cin >> n >> m;	
	memset(g, 0x3f, sizeof g);
	
	while(m--){
		int a, b, c;
		cin >> a >> b >> c;
		g[a][b] = min(g[a][b], c);
	}
		
	int t = dijkstra();
	
	cout << t << endl;
	return 0;		

}

堆优化的迪杰斯特拉算法

思想

code

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int N = 1000010;

typedef pair<int, int> PII;

// 堆优化的迪杰斯特拉算法 o(mlogn) 稀疏图(n,m 相差不大) 邻接表

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];


void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
int dijkstra()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	
	priority_queue<PII, vector<PII>, greater<PII> > heap;

	heap.push({0, 1});

	while(heap.size()){
		auto 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] == 0x3f3f3f3f) return -1;
	else return dist[n];
}


int main()
{
	
	cin >> n >> m;
	
	memset(h, -1, sizeof h);
	
	while(m--){
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
		
	int t = dijkstra();
	
	cout << t << endl;
	return 0;		

}

Bellman-Ford算法

思想

code

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 510, M = 10010;
int n, m, k;
int dist[N], backup[N];
struct Edge
{
	int a, b, w;	
}edges[M];

void bellman_ford()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	
	for(int i = 0; i < k; i++){
		memcpy(backup, dist, sizeof dist); // copy
		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);
		}
	}
    
	if(dist[n] > 0x3f3f3f3f / 2) puts("impossible");
	else cout << dist[n] << endl;
}

int main()
{
	cin >> n >> m >> k;

	for(int i = 0; i < m; i++){
		int a, b, w;
		cin >> a >> b >> w;
		edges[i] = {a, b ,w};
	}
	
	bellman_ford();
	
	return 0;		
}

SPFA 算法

思想

code - 求最短路

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int N = 1000010;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
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()
{
	memset(dist, 0x3f, sizeof dist);
	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;
				}
			} 
		}
	}
	
	if(dist[n] == 0x3f3f3f3f) puts("impossible");
	else cout << dist[n] << endl;

}

int main()
{
	
	cin >> n >> m;
	
	memset(h, -1, sizeof h);
	
	while(m--){
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
		
    spfa();
	return 0;		

}

code - 判断负环

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int N = 1000010;



int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
int cnt[N];

void add(int a, int b, int c)
{
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

bool spfa()
{
	
	
	queue<int>q;

	for(int i = 1; i <= n; i++){
		st[i] = true;
		q.push(i);
	}
	
	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];
				cnt[j] = cnt[t] + 1;
				if(cnt[j] >= n){
					return true;
				} 
				if(!st[j]){
					q.push(j);
					st[j] = true;
				}
			} 
		}
	}
	
	return false;
	
}


int main()
{
	
	cin >> n >> m;
	
	memset(h, -1, sizeof h);
	
	while(m--){
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c);
	}
		
	if(spfa()) puts("Yes");
	else puts("No");
	
	return 0;		

}

Floyd 算法

思想

code

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 210, INF = 1e9;
int n, m, Q;
int d[N][N];

void floyd()
{
	for(int k = 1; k <= n; k++){
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
			}
		}
	}
}

int main()
{
	cin >> n >> m >> Q;
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			if(i == j) d[i][j] = 0;
			else d[i][j] = INF;
		}
	}
	
	while(m--){
		int a, b, w;
		cin >> a >> b >> w;
		d[a][b] = min(d[a][b], w);
	}
	
	floyd();
	
	while(Q--){
		int a, b;
		cin >> a >> b;
		if(d[a][b] > INF / 2) cout << "impossible" << endl;
		else cout << d[a][b] << endl;
	}
	return 0;		

}

最小生成树

普利姆算法(Prim)

朴素版本

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 510, INF = 0x3f3f3f3f;

int n, m;
int g[N][N];
int dist[N];
bool st[N];

int prim()
{
	memset(dist, 0x3f, sizeof dist);
	
	int res = 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;
			}
		}
		
		if(i && dist[t] == INF) return INF;
		if(i) res += dist[t];
		
		for(int j = 1; j <= n; j++){
			dist[j] = min(dist[j], g[t][j]);
		}
	
		st[t] = true;
	}
	
	return res;
}

int main()
{
	cin >> n >> m;
	memset(g, 0x3f, sizeof g);
	
	while(m--){
		int a, b, c;
		cin >> a >> b >> c;
		g[a][b] = g[b][a] = min(g[a][b], c);
	}
	
	int t = prim();
	
	if(t == INF) cout << "impossible" << endl;
	else cout << t << endl;
    return 0;
}

堆优化版本(不常用)

  • O(mlogn)

克鲁斯卡尔算法(Kruskal)

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 200010;

int n, m;
int p[N];

struct Edge
{
	int a, b, w;
	
	bool operator<(const Edge &W)const{
		return w < W.w;
	}
}edges[N];

int find(int x)
{
	if(p[x] != x) p[x] = find(p[x]);
	return p[x];
}
int main()
{
	cin >> n >> m;
	for(int i = 0; i < m; i++){
		int a, b, w;
		cin >> a >> b >> w;
		edges[i] = {a, b, w};
	}
	
	sort(edges, edges + m);
	
	for(int i = 1; i <= n; i++){
		p[i] = i;
	}
	
	int res = 0, cnt = 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;
			res += w;
			cnt ++;
		}
	}
	
	if(cnt < n - 1) cout << "impossible" << endl;
	else cout << res << endl;
	
	return 0;
}

二分图

  • 二分图:当且仅当图中不含有奇数环

染色法

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010, M = 200010;

int n, m;
int h[N], e[M], ne[M], idx;
int color[N];

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]){
			if(!dfs(j, 3 - c)) return false;
		}else if(color[j] == c) return false;
	}
	return true;
}
int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	
	while(m--){
	    int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	
	bool flag = true;
	for(int i = 1; i <= n; i++){
		if(!color[i]){
			if(!dfs(i, 1)){
				flag = false;
				break;
			}
		}
	}
	
	if(flag) cout << "Yes" << endl;
	else cout << "No" << endl;
	
	return 0;
}

匈牙利算法

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 510, M = 100010;

int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];
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] == 0|| find(match[j])){
				match[j] = x;
				return true;
			}
		}
	}
	return false;
}

int main()
{
	
	cin >> n1 >> n2 >> m;
	
	memset(h, -1, sizeof h);
	
	while(m--){
		int a, b;
		cin >> a >> b;
		add(a, b);
	}
	
	int res = 0;
	for(int i = 1; i <= n1; i++){
		memset(st, false, sizeof st);
		if(find(i)) res++;
	}
	
	cout << res << endl;
	
	return 0;
}

背包问题

01背包问题

思想

  • 2. 01背包问题 - AcWing题库

  • 每个物品只能选一个

  • f(i,j) 从前 i 个中选,总体积不超过 j 的最大价值

  • 不含 if(i-1, j) 含 i :f(i-1,j-Vi) +Wi

  • f(i,j) = MAX(f(i-1,j),f(i-1,j-Vi)+Wi)

code

// 二维
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

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 = 0; j <= m; j++){
			f[i][j] = f[i - 1][j];
			if(j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
		}
	}
	
	cout << f[n][m] << endl;
	return 0;
}
  • 优化成一维
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;

int n, m;
int v[N], w[N];
int f[N];

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 = m; j >= v[i]; j--){
			f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}
	
	cout << f[m] << endl;
	return 0;
}

完全背包问题

思想

code

  • 朴素
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

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 = 0; j <= m; j++){
			f[i][j] = f[i - 1][j];
			if(j >= v[i]) f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]);
		}
	}
	
	cout << f[n][m] << endl;
	return 0;
}
  • 优化
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N];

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++){
			f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}
	
	cout << f[m] << endl;
	return 0;
}

多重背包问题

思想

code

  • 朴素
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 110;

int n, m;
int v[N], w[N], s[N];
int f[N][N];

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 = 0; j <= m; j++){
			for(int k = 0; k <= s[i] && k * v[i] <= j; k++){
				f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
			}
		}
	}
	
	cout << f[n][m] << endl;
	return 0;
}

二进制优化

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 25000;

int n, m;
int v[N], w[N];
int f[N];

int main()
{
	cin >> n >> m;
	
	int cnt = 0;
	for(int i = 1; i <= n; i++){
		int a, b, s;
		cin >> a >> b >> s;
		
		int k = 1;
		// 拆分成 1, 2, 4, 8, 16, ....
		while(k <= s){
			cnt++;
			v[cnt] = a * k;
			w[cnt] = b * k;
			s -= k;
			k *= 2;
		}
		if(s > 0){
			cnt++;
			v[cnt] = a * s;
			w[cnt] = b * s;
		}
	}
	
	n = cnt;
	
	// 做 01背包问题
	for(int i = 1; i <= n; i++){
		for(int j = m; j >= v[i]; j--){
			f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}
	
	cout << f[m] << endl;
	return 0;
}

分组背包问题

思想

code

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

const int N = 110;

int n, m;
int v[N][N], w[N][N], s[N];
int f[N];

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){
					f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
				}
			}
		}
	}
	
	cout << f[m] << endl;
	return 0;
}

动态规划

线性 DP

数字三角形

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

const int N = 510, INF = 1e9;

int n;
int a[N][N];
int f[N][N];

int main()
{
	cin >> n;
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++){
			cin >> a[i][j];
		}
	}
	
	for(int i = 0; i <= n; i++){
		for(int j = 0; j <= i + 1; j++){
			f[i][j] = -INF;
		}
	}
	
	f[1][1] = a[1][1];
	
	for(int i = 2; i <= n; i++){
		for(int j = 1; j <= i; j++){
			f[i][j] = max(f[i - 1][j - 1] + a[i][j], f[i - 1][j] + a[i][j]);
		}
	}
	
	int res = -INF;
	for(int i = 1; i <= n; i++){
		res = max(res, f[n][i]);
	}
	
	cout << res << endl;

	return 0;
}

最长上升子序列

朴素版本

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;

int n;
int a[N], f[N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	
	for(int i = 1; i <= n; i++){
		f[i] = 1;
		for(int j = 1; j < i; j++){
			if(a[j] < a[i]){
				f[i] = max(f[i], f[j] + 1);
			}
		}
	}
	
	int res = 0;
	for(int i = 1; i <= n; i++){
		res = max(res, f[i]);
	}
	
	cout << res << endl;
    return 0;
}

优化版本

最长公共子序列

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

const int N = 1010;

int n, m;
char a[N], b[N];
int f[N][N];

int main()
{
	cin >> n >> m;
	scanf("%s%s", a + 1, b + 1);
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			f[i][j] = max(f[i - 1][j], f[i][j - 1]);
			if(a[i] == b[j]){
				f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
			}
		}
	}
	
	cout << f[n][m] << endl;
	
	return 0;
}

最短编辑距离

区间 DP

石子合并

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 310;

int n;
int s[N];
int f[N][N];

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> s[i];
	
	for(int i = 1; i <= n; i++) s[i] += s[i - 1];
	
	for(int len = 2; len <= n; len ++){
		for(int i = 1; i + len - 1 <= n; i++){
			int l = i, r = i + len - 1;
			f[l][r] = 1e8;
			for(int k = l; k < r; k++){
				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
			}		
		}
	}
	
	cout << f[1][n] << endl;
	return 0;
}

数位统计 DP

计数问题

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

int get(vector<int> num, int l, int r)
{
	int res = 0;
	for(int i = l; i >= r; i--){
		res = res * 10 + num[i];
	}
	return res;
}

int power10(int x)
{
	int res = 1;
	while(x--){
		res *= 10;
	}
	return res;
}

int count(int n, int x)
{
	if(!n) return 0;
	
	vector<int> num;
	while(n){
		num.push_back(n % 10);
		n /= 10;
	}
	
	n = num.size();
	
	int res = 0;
	for(int i = n - 1 - !x; i >= 0; i--){
		if(i < n - 1){
			res += get(num, n - 1, i + 1) * power10(i);
			if(!x) res -= power10(i);
		}
		if(num[i] == x){
			res += get(num, i - 1, 0) + 1;
		}else if(num[i] > x){
			res += power10(i);
		}
	}
	return res;
	
}

int main()
{
	int a, b;
	while(cin >> a >> b, a || b){
		if(a > b) swap(a, b);
		for(int i = 0; i < 10; i++){
			cout << count(b, i) - count(a - 1, i) << " ";
		}
		cout << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_60955261/article/details/131101192