目录
6.P2872 [USACO07DEC] Building Roads S
1.P3366 【模板】最小生成树
1.题目
2.思路
就是最小生成树的模板,不过对于Prim 算法,要记得去重边,这是一个坑
3.代码
kruskal 算法
#include <stdio.h>
struct edge
{
int u;
int v;
int w;
};
struct edge e[200010];
int n, m;
int s[5100];
int count = 0;
int sum = 0;
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
int temp = e[left].w;
while (i != j)
{
while (e[j].w >= temp && i < j)
{
j--;
}
while (e[i].w <= temp && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
return;
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++)
{
scanf("%d %d %d", & e[i].u, &e[i].v, &e[i].w);
}
quick(1, m);
for (int i = 1; i <= n; i++)
{
s[i] = i;
}
for (int i = 1; i <= m; i++)
{
int t1 = Find(e[i].u);
int t2 = Find(e[i].v);
if (t1 != t2)
{
s[t2] = t1;
count++;
sum += e[i].w;
}
if (count == n - 1)
{
break;
}
}
if (count != n - 1)
{
printf("orz\n");
}
if (count == n - 1)
{
printf("%d\n", sum);
}
return 0;
}
Prim 算法
#include <stdio.h>
int e[5010][5010];
int book[5010] = { 0 };
int dis[5010];
int main()
{
int n, m;
int count = 0;
int sum = 0;
int inf = 99999999;//表示无穷
scanf("%d %d",&n, &m);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
{
e[i][j] = 0;
}
else
{
e[i][j] = inf;
}
}
}
for (int i = 1; i <= m; i++)//需要注意重边的情况,两个顶点之间可能有多条边,取最小的那条边存入邻接矩阵;
{
int t1, t2, t3;
scanf("%d %d %d", &t1, &t2, &t3);
if (t3 < e[t1][t2])//改进
{
e[t1][t2] = t3;
e[t2][t1] = t3;
}
}
for (int i = 1; i <= n; i++)
{
dis[i] = e[1][i];
}
book[1] = 1;
count++;
int j;
for (int i = 1; i <= n - 1; i++)
{
int min = inf;
for (int i = 1; i <= n; i++)
{
if (book[i] == 0 && dis[i] < min)
{
min = dis[i];
j = i;
}
}
if (min != inf)//如果min==inf 则没有找到最小的边,该点到达不了那几个顶点,//改进
{
book[j] = 1;
count++;
sum += dis[j];
}
for (int k = 1; k <= n; k++)
{
if (book[k] == 0 && dis[k] > e[j][k])
{
dis[k] = e[j][k];
}
}
}
if (count == n)
{
printf("%d\n", sum);
}
if (count != n)
{
printf("orz\n");
}
return 0;
}
2.P1991 无线通讯网
1.题目
2.思路
将点的坐标转化为点到点的距离。然后再套用kruskal 算法的模板;连通的边的数量只需要为P-S。就可以打印出D了。因为根据题目意思求的是用无线电的距离;减去用卫星电话的边;再求存入所有的边的最大值;
3.代码
#include <stdio.h>
#include <math.h>
struct edge
{
int u;
int v;
double w;
}e[250010];
int s[510];
int s1, p;
int a[510], b[510];
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
{
j--;
}
while (e[i].w <= e[left].w && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d", &s1, &p);
for (int i = 1; i <= p; i++)
{
scanf("%d %d", &a[i], &b[i]);
}
int n = 0;
for (int i = 1; i <= p; i++)
{
for (int j = 1; j <= p; j++)
{
n++;
e[n].w = sqrt((a[j] - a[i]) * (a[j] - a[i]) + (b[j] - b[i]) * (b[j] - b[i]));//将坐标转换成顶点到顶点的边;要用n来另外记录边数。
e[n].u = i;
e[n].v = j;
}
}
for (int i = 1; i <= p; i++)
{
s[i] = i;
}
quick(1, n);
double ans = 0;
int count = 0;
for (int i = 1; i <= n; i++)
{
int t1 = Find(e[i].u);
int t2 = Find(e[i].v);
if (t1 != t2)
{
count++;
s[t2] = t1;
ans = e[i].w;
}
if (count == p - s1)
{
break;
}
}
printf("%.2lf", ans);
return 0;
}
3.P2121 拆地毯
1.题目
2.思路
直接套用最小生成树的模板,不过题目要我们求的是最大值,不就是用最小生成树的模板求最大生成树吗?
3.代码
#include <stdio.h>
struct edge
{
int u;
int v;
int w;
};
struct edge e[100010];
int s[100010];
int n, m, k;
int count = 0;
int sum = 0;
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
while (i != j)
{
while (e[j].w <= e[left].w && i < j)
{
j--;
}
while (e[i].w >= e[left].w && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= m; i++)
{
scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
}
for (int i = 1; i <= n; i++)
{
s[i] = i;
}
quick(1, m);
for (int i = 1; i <= m; i++)
{
int t1 = Find(e[i].u);
int t2 = Find(e[i].v);
if (t1 != t2)
{
s[t2] = t1;//不要忘记合并了;
count++;
sum += e[i].w;
}
if (count == k)
{
break;
}
}
printf("%d\n", sum);
return 0;
}
4.P1396 营救
1.题目
2.思路
用kruskal 算法的模板将边从小到大排序,将边连通,直到1与3连通时,打印此时的边;此时就是最大的拥挤度了
3.代码
#include <stdio.h>
struct edge
{
int u;
int v;
int w;
};
struct edge e[20010];
int s[10100];
int n, m, s1, t;
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
{
j--;
}
while (e[i].w <= e[left].w && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d %d %d", &n, &m, &s, &t);
for (int i = 1; i <= m; i++)
{
scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
}
for (int i = 1; i <= n; i++)
{
s[i] = i;
}
quick(1, m);
for (int i = 1; i <= m; i++)
{
int a = Find(e[i].u);
int b = Find(e[i].v);
if (a != b)
{
s[a] = b;
}
if (Find(s1) == Find(t))
{
printf("%d\n", e[i].w);
break;
}
}
return 0;
}
5.P1194 买礼物
1.题目
2.思路
套用kruskal 的模板,将输入的数据转化成顶点到顶点的边值来记录;将边值从小到大排序;再遍历存储的边,不连通的,则加入树,连通且个数count++;当边的值与 a 值的较小值。最后结果打印sum+(B-count)*a 的值,因为可能题目给的数据有些边未连通;
3.代码
#include <stdio.h>
struct edge
{
int u;
int v;
int w;
};
struct edge e[2500000];
int s[5010];
int count = 0;
int sum = 0;
int a, b;
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
{
j--;
}
while (e[i].w <= e[left].w && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d", &a, &b);
int n = 0;
for (int i = 1; i <= b; i++)
{
for (int j = 1; j <= b; j++)
{
int t = 0;
scanf("%d", &t);
if (j > i && t != 0)
{
n++;
e[n].u = i;
e[n].v = j;
e[n].w = t;
}
}
}
for (int i = 1; i <= b; i++)
{
s[i] = i;
}
quick(1, n);
for (int i = 1; i <= n; i++)
{
int t1 = Find(e[i].u);
int t2 = Find(e[i].v);
if (t1 != t2)
{
s[t1] = t2;
count++;
if (a > e[i].w)
{
sum += e[i].w;
}
else
{
sum += a;
}
}
if (count == b - 1)
{
break;
}
}
printf("%d\n", sum + (b - count) * a);//加上未链接的顶点;
return 0;
}
6.P2872 [USACO07DEC] Building Roads S
1.题目
2.思路
用kruskal 算法的模板,不过有些区别,需要将m条边提前连通,表示已经加入的边;将点转换成坐标时,先记录边值的平方值;降低时间复杂度。
3.代码
#include <stdio.h>
#include <math.h>
struct edge
{
int u;
int v;
double w;
};
struct edge e[5000100];
int s[10100];
int n, m;
double a[10100], b[10100];
int count = 0;
double sum = 0;
void quick(int left, int right)
{
if (left > right)
{
return;
}
int i = left;
int j = right;
struct edge t;
while (i != j)
{
while (e[j].w >= e[left].w && i < j)
{
j--;
}
while (e[i].w <= e[left].w && i < j)
{
i++;
}
if (i < j)
{
t = e[i];
e[i] = e[j];
e[j] = t;
}
}
t = e[left];
e[left] = e[i];
e[i] = t;
quick(left, i - 1);
quick(i + 1, right);
}
int Find(int x)
{
if (s[x] == x)
{
return x;
}
else
{
return s[x] = Find(s[x]);
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lf %lf", &a[i], &b[i]);
}
int n1 = 0;
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
n1++;//先用平方记录,降低时间复杂度,主要是这里卡不过
e[n1].w = (a[j] - a[i]) * (a[j] - a[i]) + (b[j] - b[i]) * (b[j] - b[i]);//将坐标转换成顶点到顶点的边;要用n来另外记录边数。
e[n1].u = i;
e[n1].v = j;
}
}
for (int i = 1; i <= n; i++)
{
s[i] = i;
}
for (int i = 1; i <= m; i++)//提前连通一些边
{
int x, y;
scanf("%d %d", &x, &y);
int t1 = Find(x);
int t2 = Find(y);//提前连通边;
if (t1 != t2)
{
count++;
s[t2] = t1;
}
}
quick(1, n1);
for (int i = 1; i <= n1; i++)
{
int t1 = Find(e[i].u);
int t2 = Find(e[i].v);
if (t1 != t2)
{
count++;
s[t2] = t1;
sum += sqrt(e[i].w);
}
if (count == n - 1)
{
break;
}
}
printf("%.2lf", sum);
return 0;
}
7.P1359 租用游艇
1.题目
2.思路
求单源最短路径问题;因为是带权图,所以考虑用迪杰斯特拉算法;最后打印1到n的最短距离;
3.代码
#include <stdio.h>//求的是1到n的距离,最短单源路径;dj,带权图;
int e[210][210];
int dis[210];
int book[210] = { 0 };
int n;
int inf = 1000100;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
{
e[i][j] = 0;
}
else
{
e[i][j] = inf;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
int a = 0;
scanf("%d", &a);
e[i][j] = a;
}
}
for (int i = 1; i <= n; i++)
{
dis[i] = e[1][i];
}
book[1] = 1;
int j;
for (int i = 1; i < n; i++)
{
int min = inf;
for (int h = 1; h <= n; h++)
{
if (book[h] == 0 && dis[h] < min)
{
min = dis[h];
j = h;
}
}
if (min != inf)
{
book[j] = 1;
for (int k = 1; k <= n; k++)
{
if (book[k] == 0 && dis[k] > dis[j] + e[j][k])
{
dis[k] = dis[j] + e[j][k];
}
}
}
}
printf("%d\n", dis[n]);
return 0;
}