整理的算法模板合集: ACM模板
目录
一、kruskal算法
给出一个无向图,求出最小生成树
typedef pair<int,int> PII;
const int N = 2e5+7;
struct node{
int x,y,z;
bool operator<(node &t)const{
return z < t.z;
}
}edge[N];
int fa[N],n,m,ans;
int Find(int x){
if(x == fa[x])return x;
return fa[x] = Find(fa[x]);
}
int main()
{
cin>>n>>m;
over(i,1,m)
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
sort(edge + 1,edge + 1 + m);
over(i,1,n)
fa[i] = i;
over(i,1,m){
int x = Find(edge[i].x);
int y = Find(edge[i].y);
if(x == y)continue;
fa[x] = y;
ans += edge[i].z;
}
printf("%d\n",ans);
}
二、prim算法
朴素写法,拿n-1条边,建议使用。
有时候普通的版本可以过,但是堆优化版本就会wa,所以尽量用kruskal,实在不行了就用朴素版本。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 507, M = 5000007, INF = 0x3f3f3f3f;
int g[N][N], dist[M], n, m, ans;
bool vis[N];
int prim(){
memset(dist, 0x3f, sizeof dist);
memset(vis, 0, sizeof vis);
dist[1] = 0;
int res = 0;
for(int i = 0;i < n; ++ i){
int t = 0;
for(int j = 1; j <= n; ++ j){
if(!vis[j] && (t == 0 || dist[j] < dist[t])){
t = j;
}
}
if(i && dist[t] == INF)return INF;
if(i)res += dist[t];
vis[t] = 1;
for(int y = 1; y <= n; ++ y){
if(!vis[y])dist[y] = min(dist[y], g[t][y]);
}
}
return res;
}
int main(){
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
for(int i = 1; i <= n; ++ i)g[i][i] = 0;
for(int i = 1; i <= m; ++ i){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
g[x][y] = g[y][x] = min(g[x][y], z);
}
if(prim() == INF)puts("impossible");//不存在最小生成树
else {
for(int i = 2; i <= n; ++ i)
ans += dist[i];
if(ans == INF)puts("impossible");
else printf("%d\n", ans);
}
return 0;
}
堆优化版本。
typedef long long ll;
typedef pair<int,int> PII;
const int N = 4e5+7;
int ver[N],nex[N],edge[N],head[N],tot;
int n,m,ans;
int dis[N];
int vis[N],cnt;
void add(int u,int v,int val){
ver[++tot] = v;
edge[tot] = val;
nex[tot] = head[u];
head[u] = tot;
}
priority_queue<PII,vector<PII>,greater<PII> >q;
void prim(){
dis[1] = 0;
q.push({
0,1});
while(q.size()&&cnt != n){
int d = q.top().first,u = q.top().second;
q.pop();
if(vis[u])continue;
cnt++;
ans += d;
vis[u] = 1;
for(int i = head[u];i;i = nex[i]){
int v = ver[i];
if(edge[i] < dis[v])
dis[v] = edge[i],q.push({
dis[v],v});
}
}
}
int main()
{
memset(dis,0x3f,sizeof dis);
scanf("%d%d",&n,&m);
over(i,1,m){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
prim();
if(cnt == n)
printf("%d\n",ans);
else puts("orz");
return 0;
}
三、Boruvka算法
有 n 个点排成一行,在第 i,j 个点之间连边的代价为 ∣ i − j ∣ × D + A i + A j |i-j| \times D+A_i+A_j ∣i−j∣×D+Ai+Aj,求将它们连成一棵树的最小代价。( 1 ≤ n ≤ 1 0 5 1\leq n \leq 10^5 1≤n≤105)
直接 O ( n 2 ) O(n^2) O(n2)建图会T,而Boruvka算法正适用于此类题目,可以在 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度内解决问题
考虑维护当前的连通块(初始每个点为独立的一个连通块)
对每个连通块,找到一条与该连通块相连的,且另一端点不在此连通块中的边权最小的边。
将所有的这些边都加入最小生成树,注意,当加入一条边时需判断该边的两端点是否在同一连通块。
重复若干遍上述操作,直到图连通。
/*Atcoder keyence2019 E*/
#include <cstdio>
#include <iostream>
#include <cstring>
#define MN 201000
typedef long long ll;
int fa[MN];
int c1[MN], c2[MN];
int Min[MN];
ll D; int a[MN];
int X[MN], Y[MN];
int x[MN], y[MN];
ll ans = 0;
int bl;
int n;
int Abs(int a) {
return a > 0 ? a : -a;}
ll F(int i) {
return i ? i * D + a[i] : 1e18;}
ll G(int i) {
return i ? -i * D + a[i] : 1e18;}
ll H(int i, int j) {
if(i == 0 || j == 0) return 1e18; return Abs(i - j) * D + a[i] + a[j];}
void add(int *c, int x, int v, int type)
{
for(int i = x; i <= n; i += i & -i)
{
if(!type) if(F(v) < F(c[i])) c[i] = v;
if( type) if(G(v) < G(c[i])) c[i] = v;
}
}
int query(int *c, int x, int type)
{
int ans = 0;
for(int i = x; i; i -= i & -i)
{
if(!type) if(F(c[i]) < F(ans)) ans = c[i];
if( type) if(G(c[i]