![在这里插入图片描述](https://img-blog.csdnimg.cn/eb0851823ad2478082db3715ea653ccb.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAV1RjcmF6eSAu,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
Kruskal算法
用于稀疏图(即边少的图),用到并查集的知识
struct wt//**一击即中yes**//
{
int x, y, z;
} e[N];
int n, m, f[N], cnt, sum;
**********************************************************************************
for (int i = 1; i <= m; i++)
cin >> e[i].x >> e[i].y >> e[i].z;//读入边
for (int i = 1; i <= n; i++)
f[i] = i;//初始化每个集合一个顶点
**********************************************************************************
int getfa(int v){return f[v] == v ? v : getfa(f[v]);}
bool merge(int a, int b)
{//归并,判断一条边的两点是否在一个集合中形成回路
int fa = getfa(a);
int fb = getfa(b);
if (fa != fb)
{
f[fb] = fa;
return 0;
}
else
return 1;
}
int kruskal()//克鲁斯卡尔算法
{
sort(e + 1, e + 1 + m, cmp);//先将边从小到大排序
sum = 0;//最小生成树权值之和
for (int i = 1; i <= m; i++)
{
if (!merge(e[i].x, e[i].y))//判断是否形成回路
{
sum += e[i].z;
cnt++;//记录加入的边数
}
if (cnt == n - 1)//边数为n-1则退出循环
break;
}
return sum;//返回权值之和
}
牛客一道小栗子走廊泼水节
大概题意是给一个最小生成树,让你求添加边构成完全图的最小权值【贪心地,考虑两个连通块,形成完全图需要a*b条边】
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll t, n, ans;
ll f[N], siz[N];
struct edge
{
ll u, v, w;
bool operator<(const edge p) const { return w < p.w; }//运算符重载
} e[N];
ll getfa(ll v) { return f[v] == v ? v : f[v] = getfa(f[v]); }
void init()
{
for (int i = 1; i <= n; i++)
{
f[i] = i;
siz[i] = 1;
}
ans = 0;
}
void kruskal()
{
init(); //注意初始化的位置
sort(e + 1, e + n);//注意排序的位置
for (int i = 1; i < n; i++)
{
ll fax = getfa(e[i].u), fay = getfa(e[i].v);
if (fax != fay)//如果两个不在同一集合,则需都联通
{
ans += (siz[fax] * siz[fay] - 1) * (e[i].w + 1);//贪心+思维
if (siz[fax] < siz[fay])
swap(fax, fay);
f[fay] = fax;
siz[fax] += siz[fay];
}
}
}
int main()
{
// IOS;
scanf("%lld", &t);
while (t--)
{
scanf("%lld", &n);
for (int i = 1; i < n; i++)
{
ll u, v, w;
scanf("%lld%lld%lld", &u, &v, &w);
e[i].u = u, e[i].v = v, e[i].w = w;
}
kruskal();
printf("%lld\n", ans);
}
return 0;
}
Prim算法
贪心算法,时间复杂度为O[N^2],用于稠密图(即边多的情况)可以用二叉堆优化
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
e[i][j] = e[j][i] = (i == j ? 0 : INT_MAX);//初始化边***很重要
***************************************************************************************************
int n, m, cnt; //n是顶点个数,m是边数
int e[N][N], dis[N]; //dis是每个顶点到最小生成树的最小距离
bool vis[N]; //将加入最小生成树的点标记
int prim() //普里姆算法求最小生成树,不要轻易cnt++,有乱子
{
int sum = 0; //最小生成树之和
for (int i = 1; i <= n; i++)
dis[i] = e[1][i]; //初始化每个顶点到最小生成树的距离为到顶点1的距离
vis[1] = 1; //将1顶点加入最小生成树
for (int i = 1; i < n; i++) //寻找剩余的n-1个顶点
{
int mif = 0, mi = INT_MAX;
for (int j = 1; j <= n; j++) //寻找距离最小生成树最近的顶点
{
if (dis[j] < mi && !vis[j]) //找到且该点未在生成树中
{
mi = dis[j];
mif = j; //记录该点
}
}
sum += dis[mif]; //之和累加
vis[mif] = 1; //将该点标记,加入最小生成树中
for (int j = 1; j <= n; j++) //更新每个未在生成树中的点到最小生成树的最近距离
{
if (!vis[j] && e[mif][j] < dis[j])
dis[j] = e[mif][j];
}
}
return sum;
}
牛客一道小栗子黑暗城堡
即求最短路径生成树,题意是让你统计以1为源点的最短路径树的方案数
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4 + 100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, m, tot, ans = 1;
ll to[M], val[M], nex[M], head[M], cnt[N], dis[N];
bool vis[N];
void add(ll u, ll v, ll w)
{
to[++tot] = v;
val[tot] = w;
nex[tot] = head[u];
head[u] = tot;
}
void init(){memset(dis, inf, sizeof(dis));}
void dijkstra()
{
init();
priority_queue<pll, vector<pll>, greater<pll>> p;
p.push({0, 1});//pair{dis[i],i}
dis[1] = 0;
while (p.size())
{
pll x = p.top();
p.pop();
if (vis[x.second])
continue;
for (int i = head[x.second]; i; i = nex[i])
{
ll y = to[i], z = val[i];
if (dis[y] > dis[x.second] + z)
{
dis[y] = dis[x.second] + z;
p.push({dis[y], y});
}
}
}
}
int main()
{
// IOS;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= m; i++)
{
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
dijkstra();
for (int i = 1; i <= n; i++)
{
for (int j = head[i]; j; j = nex[j])
{
ll y = to[j], z = val[j];
if (dis[y] == dis[i] + z)
cnt[y]++;
}
}
// for (int i = 1; i <= n; i++)
// printf("%d ", cnt[i]);
// puts("");
for (int i = 2; i <= n; i++) //注意从2开始,
ans = (ans * cnt[i]) % mod;
printf("%lld\n", ans);
return 0;
}