欢迎光临
我们一直在努力

C++图论基础最小生成树经典OJ题流食般投喂

本篇标红的字段均是可以升级的经验条呦~


OJ题来源:洛谷

OJ题名:买礼物

OJ题归属:图论基础【最小生成树】

        解题算法:kruskal算法(kk算法)

经验总结:用kurskal算法构造出来的生成树可能有若干个,顶点有不连通的情况不影响他建树,比如:a-a   a-a   a-a

                                   |

                                   a-a

kruskal算法的关键变量cnt与节点n是有关系的,cnt变量记录选的条数,n是节点个数,n – cnt 的差值就是构建出生成树的具体个数。

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 500 * 500 + 10;

int pos;
int a, n;
int fa[N]; // 并查集
struct node
{
int x, y, z;
}e[N];
int ret, cnt;

int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}

bool cmp(node& a, node& b)
{
return a.z < b.z;
}

void kk()
{
sort(e + 1, e + 1 + pos, cmp);

for (int i = 1; i <= pos; i++)
{
int x = e[i].x, y = e[i].y, z = e[i].z;

int fx = find(x), fy = find(y);
if (fx != fy)
{
cnt++;
ret += z;
fa[fx] = fy;
}
}
}

int main()
{
cin >> a >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
int k; cin >> k;
if (i >= j || k > a || k == 0) continue;
pos++;
e[pos].x = i, e[pos].y = j, e[pos].z = k;
}
}

// 并查集初始化
for (int i = 1; i <= n; i++) fa[i] = i;

kk(); // 求得ret/cnt

cout << ret + (n – cnt) * a << endl;

return 0;
}


OJ题来源:洛谷

OJ题名:繁忙的都市

OJ题归属:图论基础【最小生成树】

        解题算法:kruskal算法(kk算法)

此题有三个限制条件,前两个条件翻译出来是要求是一棵生成树,最后一个翻译出来是要是一颗瓶颈生成树。

经验总结:

瓶颈生成树的概念:在所有生成树中,最大边权值最小的生成树。

瓶颈生成树的定理:最小生成树就是瓶颈生成树。

最小生成树 ret维护的是边权和最小
瓶颈生成树 ret维护的是 n – 1 条边中,边权最大值

#include<iostream>
#include<algorithm>

using namespace std;

const int N = 310, M = 8010;

int n, m;
struct node
{
int x, y, z;
}a[M];
int fa[N]; // 并查集维护已选的顶点

int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}

int ret; // 维护 n – 1 条边中,边权最大值

bool cmp(node& a, node& b)
{
return a.z < b.z;
}

void kk()
{
sort(a + 1, a + 1 + m, cmp);

for (int i = 1; i <= m; i++)
{
int x = a[i].x, y = a[i].y, z = a[i].z;

int fx = find(x), fy = find(y);
if (fx != fy)
{
ret = max(ret, z);
fa[fx] = fy;
}
}
}

int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++) cin >> a[i].x >> a[i].y >> a[i].z;

// 并查集初始化
for (int i = 1; i <= n; i++) fa[i] = i;

cout << n – 1 << " ";

kk();

cout << ret << endl;

return 0;
}


OJ题来源:洛谷

OJ题名:滑雪

OJ题归属:图论基础【最小生成树】

        解题算法:dfs + kruskal算法(kk算法)

经验总结:最小生成树在概念上是针对于无向图的,但是,这题向我们表明,特殊情景下的有向图也是适用的,这题的图是一个有向图,但是题中提到的时间胶囊的概念,其实功能就是回溯,是的,这样我们就可以把这个有向图可以看成可以递归的二叉树。此题的各个顶点之间不确定是不是连通的,所以不是所有的边都需要记录,我们只记录由1这个节点递归过程中可以经历的边,对这些边进行排序、选边操作。此题kk算法的排序有改变,滑雪滑雪,排序的第一关键字就是高度,由高到低,高度一致时,在按边权由小到大排序。

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 + 10, M = 2e6 + 10;

int n, m;
int h[N]; // 存该景点高度
vector<PII> a[N]; // 存图
int fa[N]; // 并查集
int pos; // 在选择范围内的边数
struct node
{
int x, y, z;
}e[M]; // 存储在选择范围内的边
LL cnt, ret; // 景点数 路径和
bool st[N];

int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void dfs(int u)
{
cnt++;
st[u] = true;

for (auto& p : a[u])
{
// 存储在选择范围内的边
int y = p.first, z = p.second;
pos++;
e[pos].x = u, e[pos].y = y, e[pos].z = z;

if (!st[y]) dfs(y);
}
}

bool cmp(node& a, node& b)
{
int ay = a.y, by = b.y;

// 排序第一关键字:高度,第二关键字:边权
if (h[ay] != h[by]) return h[ay] > h[by];
else return a.z < b.z;
}

void kk()
{
sort(e + 1, e + 1 + m, cmp);

// 选边
for (int i = 1; i <= pos; i++)
{
int x = e[i].x, y = e[i].y, z = e[i].z;
int fx = find(x), fy = find(y);
if (fx != fy)
{
ret += z;
fa[fx] = fy;
}
}
}

int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> h[i];
for (int i = 1; i <= m; i++)
{
int x, y, z; cin >> x >> y >> z;

//判断哪些边可以存
if (h[x] >= h[y]) a[x].push_back({ y, z });
if (h[y] >= h[x]) a[y].push_back({ x, z });
}

// 并查集初始化
for (int i = 1; i <= n; i++) fa[i] = i;

dfs(1);
cout << cnt << " ";

kk();
cout << ret << endl;

return 0;
}


感谢~

赞(0)
未经允许不得转载:171主机测评 » C++图论基础最小生成树经典OJ题流食般投喂
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址