1348:【例4-9】城市公交网建设问题
【题目描述】
有一张城市地图,图中的顶点为城市,无向边代表两个城市间的连通关系,边上的权为在这两个城市之间修建高速公路的造价,研究后发现,这个地图有一个特点,即任一对城市都是连通的。现在的问题是,要修建若干高速公路把所有城市联系起来,问如何设计可使得工程的总造价最少?
【题目分析】
最小生成树模板题,Kruskal算法和Prim算法均可以
【代码实现】
Prim算法
#include<bits/stdc++.h>
using namespace std;
struct node {
int from;
int to;
int dis;
friend bool operator<(node a, node b) {
return a.dis >= b.dis;
}
};
vector<node> edge[105];
priority_queue<node> que;
bool vis[105];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y, w;
cin >> x >> y >> w;
edge[x].push_back({x, y, w});
edge[y].push_back({y, x, w});
}
//init priority_queue
vis[1] = 1;
for (auto e : edge[1]) {
que.push(e);
}
//loop resolve
while (!que.empty()) {
node a = que.top();
que.pop();
if (!vis[a.to]) {
vis[a.to] = 1;
cout << a.from << " " << a.to << endl;
for (auto e : edge[a.to]){
que.push(e);
}
}
}
return 0;
}
Kruskal算法
#include<bits/stdc++.h>
using namespace std;
struct node {
int from, to, dis;
};
bool cmp(node &a, node &b) {
return a.dis < b.dis;
}
vector<node> edge;
int fa[105];
int find(int a) {
return fa[a] == a ? a : fa[a] = find(fa[a]);
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
fa[i] = i;
}
for (int i = 1; i <= m; i++) {
int x, y, w;
cin >> x >> y >> w;
edge.push_back({x, y, w});
edge.push_back({y, x, w});
}
sort(edge.begin(), edge.end(), cmp);
for (auto e : edge) {
int from = find(e.from);
int to = find(e.to);
if (from != to) {
cout << e.from << " " << e.to << endl;
fa[from] = to ;
}
}
return 0;
}
1349:【例4-10】最优布线问题
【题目描述】
学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。
当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的连接。
现在由你负责连接这些计算机,任务是使任意两台计算机都连通(不管是直接的或间接的)。
【题目分析】
最小生成树模板题,Kruskal算法和Prim算法均可
【代码实现】
Prim算法
#include <bits/stdc++.h>
#define MAXN 101
using namespace std;
const int inf = 0x3f3f3f3f;
bool vis[MAXN];
int dis[MAXN], e[MAXN][MAXN];
int ans = 0;
int n, m;
int main() {
//input data
//clock_t s = clock();
//init
cin >> n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&e[i][j]);
}
}
for (int i = 0; i <= n; i++) {
dis[i]=inf;
}
dis[1] = 0;
for (int i = 1; i <= n; i++) {
int k = 0;
for (int j = 1; j <= n; j++) { //寻找与当前构造完的生成树最近的点
if (vis[j]) continue;
if (dis[j] < dis[k]) k = j;
}
ans += dis[k];
vis[k] = true;
for (int j = 1; j <= n; j++) { //更新其他点到生成树的距离
if (e[k][j] < dis[j]) {
dis[j] = e[k][j];
}
}
}
cout<<ans;
//output time
//cout <<endl<< clock() - s<<endl;
return 0;
}
1350:【例4-11】最短网络(agrinet)
【题目描述】
农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过100000100000。
【题目分析】
最小生成树模板题,Kruskal算法和Prim算法均可
【代码实现】
Prim算法
#include <bits/stdc++.h>
#define MAXN 101
using namespace std;
const int inf = 0x3f3f3f3f;
bool vis[MAXN];
int dis[MAXN], e[MAXN][MAXN];
int ans = 0;
int n, m;
int main() {
cin >> n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&e[i][j]);
}
}
for (int i = 0; i <= n; i++) {
dis[i]=inf;
}
dis[1] = 0;
for (int i = 1; i <= n; i++) {
int k = 0;
for (int j = 1; j <= n; j++) { //寻找与当前构造完的生成树最近的点
if (vis[j]) continue;
if (dis[j] < dis[k]) k = j;
}
ans += dis[k];
vis[k] = true;
for (int j = 1; j <= n; j++) { //更新其他点到生成树的距离
if (e[k][j] < dis[j]) {
dis[j] = e[k][j];
}
}
}
cout<<ans;
return 0;
}
1351:【例4-12】家谱树
【题目描述】
有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。
给出每个人的孩子的信息。
输出一个序列,使得每个人的后辈都比那个人后列出。
【题目分析】
拓扑排序模板题
方法1:移除入度为0的点,并将相邻点的入度减1,依次循环直到所有点都被输出
方法2:对所有的点进行深搜,在每个点返回时加入栈,搜索完成后依次弹出点
【代码实现】
方法1:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1001;
int n;
vector<int> p[maxn];
int indeg[maxn];
int vis[maxn];
queue<int> Q;
int main() {
cin >> n ;
for (int i = 1; i <= n; i++) {
int x;
while (cin >> x, x != 0) {
p[i].push_back(x);
indeg[x]++;
}
}
for (int i = 1; i <= n; i++) { //找到起点
if (indeg[i] == 0) Q.push(i);
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
cout << u << " ";
for (int i = 0; i < (int ) p[u].size(); i++) { //依次找后续节点
if (--indeg[p[u][i]] == 0)
Q.push(p[u][i]);
}
}
return 0;
}
方法2:
#include <bits/stdc++.h>
using namespace std;
vector<int> p[105];
stack<int> sta;
bool vis[105];
void dfs(int x) {
for (auto e : p[x]) {
if (vis[e]) continue;
vis[e] = 1;
dfs(e);
}
sta.push(x);
}
int main() {
//input data
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
int x;
while (cin >> x, x != 0) p[i].push_back(x);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) vis[i] = 1, dfs(i);
}
while (!sta.empty()) cout << sta.top() << " ", sta.pop();
return 0;
}
1391:局域网(net)
【题目描述】
某个局域网内有n(n≤100)台计算机,由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用f(i,j)表示i,j之间连接的畅通程度(f(i,j)≤1000),f(i,j)值越小表示i,j之间连接越通畅,f(i,j)为00表示i,j之间无网线连接。现在我们需要解决回路问题,我们将除去一些连线,使得网络中没有回路,并且被除去网线的Σf(i,j)最大,请求出这个最大值。
【题目分析】
使得网络中没有回路,并且被除去网线的Σf(i,j)最大,即留下的网线可以生成一个最小生成树,将所有网线的权值相加减去最小生成树的权值,得到最大的移除权值。
算法实现:Kruskal算法或者Prim算法求解最小生成树的权值和
【代码实现】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1001;
struct Edges {
int u;
int v;
int w;
} edge[maxn * 50];
inline bool cmp(const Edges &a, const Edges &b) {
return a.w < b.w;
}
int n, m;
int father[maxn];
int find(int x) {
if (father[x] != x)
father[x] = find(father[x]);
return father[x];
}
int ans = 0;
int main() {
//input data
//clock_t s = clock();
int sum = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> edge[i].u >> edge[i].v >> edge[i].w;
sum += edge[i].w;
}
sort(edge + 1, edge + m + 1, cmp);
for (int i = 1; i <= n; i++) {
father[i] = i;
}
int _count = 0;
for (int i = 1; i <= m; i++) {
int fa_u = find(edge[i].u);
int fa_v = find(edge[i].v);
if (fa_u != fa_v) {
ans += edge[i].w;
father[fa_u] = fa_v;
_count++;
if (_count >= n - 1) break;
}
}
cout << sum - ans;
//output time
//cout <<endl<< clock() - s<<endl;
return 0;
}
1392:繁忙的都市(city)
【题目描述】
城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两个交叉路口之间最多有一条道路相连接。这些道路是双向的,且把所有的交叉路口直接或间接的连接起来了。每条道路都有一个分值,分值越小表示这个道路越繁忙,越需要进行改造。但是市政府的资金有限,市长希望进行改造的道路越少越好,于是他提出下面的要求:
1.改造的那些道路能够把所有的交叉路口直接或间接的连通起来。
2.在满足要求1的情况下,改造的道路尽量少。
3.在满足要求1、2的情况下,改造的那些道路中分值最大值尽量小。
作为市规划局的你,应当作出最佳的决策,选择那些道路应当被修建。
【题目分析】
根据题意:将所有交叉路口直接或间接的连接起来,并使得所有路口的分值之和最小,就是求连接所有交叉路口的最小生成树。
算法实现:Kruskal算法或者Prim算法求解最小生成树的权值和
【代码实现】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1001;
struct Edges {
int u;
int v;
int w;
} edge[maxn * 50];
inline bool cmp(const Edges &a, const Edges &b) {
return a.w < b.w;
}
int n, m;
int father[maxn];
int find(int x) {
if (father[x] != x)
father[x] = find(father[x]);
return father[x];
}
int ans = 0;
int main() {
//input data
//clock_t s = clock();
int sum = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> edge[i].u >> edge[i].v >> edge[i].w;
sum += edge[i].w;
}
sort(edge + 1, edge + m + 1, cmp);
for (int i = 1; i <= n; i++) {
father[i] = i;
}
int maxw = 0;
int _count = 0;
for (int i = 1; i <= m; i++) {
int fa_u = find(edge[i].u);
int fa_v = find(edge[i].v);
if (fa_u != fa_v) {
maxw = max(maxw, edge[i].w);
father[fa_u] = fa_v;
_count++;
if (_count >= n - 1) break;
}
}
cout << n - 1 << " " << maxw;
return 0;
}
1393:联络员(liaison)
【题目描述】
Tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvj网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员两两都可以联络(直接或者是间接都可以)。Tyvj是一个公益性的网站,没有过多的利润,所以你要尽可能的使费用少才可以。
目前你已经知道,Tyvj的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。数据保证给出的通行渠道可以让所有的管理员联通。
【题目分析】
必选的通信渠道相加,将连接点进行并查集合并,对可选的通讯渠道排序,依次选择可选渠道使得所有点连通,Kruskal算法思想
【代码实现】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
struct Edges {
int u;
int v;
int w;
} edge[maxn * 50];
inline bool cmp(const Edges &a, const Edges &b) {
return a.w < b.w;
}
int n, m;
int father[maxn];
int find(int x) {
if (father[x] != x)
father[x] = find(father[x]);
return father[x];
}
int ans = 0;
int main() {
//input data
cin >> n >> m;
int index = 0;
int _count = 0;
for (int i = 1; i <= n; i++) { //init bing cha ji
father[i] = i;
}
for (int i = 1; i <= m; i++) {
int x, y, q, w;
cin >> q >> x >> y >> w;
if (q == 1) { //direct solve
ans += w;
int fa_x = find(x);
int fa_y = find(y);
if (fa_x != fa_y) {
father[fa_y] = fa_x;
_count++;
}
}
if (q == 2) { //save data
edge[++index].u = x;
edge[index].v = y;
edge[index].w = w;
}
}
//sort and add edges
sort(edge + 1, edge + index + 1, cmp);
for (int i = 1; i <= index; i++) {
// cout << edge[i].w << " ";
int fa_u = find(edge[i].u);
int fa_v = find(edge[i].v);
if (fa_u != fa_v) {
ans += edge[i].w;
father[fa_v] = fa_u;
_count++;
if (_count >= n - 1) break;
}
}
//cout answers
cout << ans;
return 0;
}
1394:连接格点(grid)
【题目描述】
有一个M行N列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。
【题目分析】
根据已经相连的线,将线的两端点进行并查集合并,枚举竖边将端点并查集合并,费用加1,枚举横边将端点并查集合并,费用加2,输出结果 Kruskal算法思想
【代码实现】
#include <bits/stdc++.h>
using namespace std;
struct node {
int x, y;
};
node fa[1005][1005];
node find(node a) {
if (a.x == fa[a.x][a.y].x && a.y == fa[a.x][a.y].y) return a;
return fa[a.x][a.y] = find(fa[a.x][a.y]);
}
bool merge(node a, node b) {
node fa_a = find(a);
node fa_b = find(b);
if (fa_a.x != fa_b.x || fa_a.y != fa_b.y) {
fa[fa_a.x][fa_a.y] = fa_b;
return true;
}
return false;
}
int main() {
//input data
int m, n;
cin >> m >> n;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
fa[i][j] = {i, j};
}
}
int sx, sy, fx, fy;
while (cin >> sx >> sy >> fx >> fy) {
merge({sx, sy}, {fx, fy});
}
int sum = 0;
//先枚举竖边
for (int i = 1; i < m; i++) {
for (int j = 1; j <= n; j++) {
if (merge({i, j}, {i + 1, j})) sum++;
}
}
//再枚举横边
for (int i = 1; i <= m; i++) {
for (int j = 1; j < n; j++) {
if (merge({i, j}, {i, j + 1})) sum += 2;
}
}
cout << sum << endl;
return 0;
}