原题链接:https://vjudge.net/contest/351234#problem/H
给出城镇数目和已经修完的路的起始点,为使城镇相互可直接或间接通达,最少还需要修建多少的路。
这是连通图问题,一般用并查集。
找出相连点的代表点(代表根),代表点不同,则互不相通,n个不同的代表点只需要n-1条路就可以相互通达。并查集可以找到不同的代表点。
#include
#include
#define INF 100001
//并查集
using namespace std;
//各点关系
int a_b[1001];
//
int check[1001];
//找x的代表根
int find_f(int x)
{
int r = x;
while(r != a_b[r])
r = a_b[r];
return r;
}
//使x和y建立关系
void con(int x,int y)
{
int fx, fy;
fx = find_f(x);
fy = find_f(y);
if(fx != fy)
{
a_b[fx] = fy;
}
}
int main()
{
int n, m;
int a, b, ans;
int i;
scanf("%d",&n);
while(n)
{
//初始化
ans = 0;
for(i=1; i<=n; i++){
a_b[i] = i;
check[i] = 0;
}
scanf("%d",&m);
//没有路,则最少需要建n-1条路
if(m == 0){
printf("%d\n",n-1);
scanf("%d",&n);
continue;
}
for(i=1; i<=m; i++){
scanf("%d%d",&a,&b);
//建立关系
con(a,b);
}
//找出全部点有几个代表根,代表根不同,则互不相通
for(i=1; i<=n; i++){
check[find_f(i)]++;
}
for(i=1; i<=n; i++){
if(check[i]) ans++;
}
//ans个不同的代表根之间需要ans-1条路
printf("%d\n",ans-1);
scanf("%d",&n);
}
return 0;
}
原题链接:https://vjudge.net/contest/351234#problem/I
给出城镇数目和距离,为使城镇相互可直接或间接通达,最少需要修建路的长度。
这是最小生成树,一般用Kruskal算法。
给每两个城镇间的距离排序,贪心地选取最小的距离构建最小生成树,答案即为选取的距离之和。
#include
#include
#include
#define INF 99999999
//Kruskal算法
using namespace std;
struct edge
{
int from;
int to;
int s;
};
//两点距离
edge dist[5000];
int n, m;
int ans;
//已经建立的关系
int from_to[101];
bool cmp(edge x, edge y)
{
if(x.s < y.s) return true;
else return false;
}
//找代表根
int find_v(int x)
{
int r = x;
while(from_to[r] != r) r = from_to[r];
return r;
}
//建立x,y关系
void coo(int x, int y)
{
int fx, fy;
fx = find_v(x);
fy = find_v(y);
if(fx != fy) from_to[fx] = fy;
}
int main()
{
int a, b;
int i;
scanf("%d",&n);
while(n)
{
//初始化
ans = 0;
for(i=1; i<=n; i++){
from_to[i] = i;
}
//确定m值
m = n * (n-1) / 2;
for(i=1; i<=m; i++){
scanf("%d%d%d", &dist[i].from, &dist[i].to, &dist[i].s);
}
sort(dist+1,dist+1+m,cmp); //以距离为依据排序,注意从1开始
for(i=1; i<=m; i++){
a = dist[i].from;
b = dist[i].to;
if(find_v(a) != find_v(b)){ //对不同的两个城镇建立关系
coo(a,b);
ans += dist[i].s; //总路长
}
}
printf("%d\n",ans);
scanf("%d",&n);
}
return 0;
}
原题链接:https://vjudge.net/contest/351234#problem/J
给出城镇数目和一些城镇间的距离,问某两个城镇之间是否可达,可以则输出最小路径。
这是任意两点最短路径问题,一般用Floyd-Warshall算法。
通过三重的for循环,将每两个点之间的距离求出。
#include
#include
#include
#define INF 11111111
//Floyd-Warshall算法
using namespace std;
int n, m;
int dist[201][201];
int main()
{
int a, b, c;
int S, T;
int i, j, k;
while(scanf("%d%d",&n,&m) != EOF)
{
//初始化
for(i=0; i<n; i++){
for(j=0; j<n; j++){
if(i == j) dist[i][j] = 0;
else dist[i][j] = INF;
}
}
for(i=0; i<m; i++){
scanf("%d%d%d",&a,&b,&c);
if(c < dist[a][b]) dist[a][b] = dist[b][a] = c;
//注意题目说的每两个城镇都有很多路,选最短
}
scanf("%d%d",&S,&T);
//这一步求出了每两个点的最短路径
for(k=0; k<n; k++)
for(i=0; i<n; i++)
for(j=0; j<n; j++){//点i能否通过k与j相连,可以则计算最小距离
if(dist[i][j] > dist[i][k] + dist[k][j]){
dist[i][j] = dist[i][k] + dist[k][j];
}
}
if(dist[S][T] < INF) printf("%d\n",dist[S][T]);//S到T的距离
else printf("%d\n",-1);
}
return 0;
}
原题链接:https://vjudge.net/contest/351234#problem/K
给出小岛的坐标,符合距离条件的两个小岛可建桥,建桥的费用与距离成正比,问建桥后所以小岛是否可有直接或间接通达,可以则输出最小费用。
这同样是最小生成树,用Kruskal算法。
给每两个小岛间的距离排序,注意筛选掉不满足条件的距离,贪心地选取最小的距离构建最小生成树,答案即为选取的距离之和。
#include
#include
#include
#include
//Kruskal算法
using namespace std;
struct island
{
int x;
int y;
};
island dao[101];//岛屿坐标
struct edge
{
int u;
int v;
double len;
};
edge e[5000];//岛与岛之间的距离的平方
int d[101];//哪些岛可建桥
int find_r(int x)//找到代表根
{
int r = x;
while(d[r] != r) r = d[r];
return r;
}
int square(int x)//平方
{
return x * x;
}
bool cmp(edge x, edge y)//依据距离,从小到大
{
if(x.len < y.len) return true;
else return false;
}
int main()
{
int t;
int n;
int fx, fy;
double dist, ans;
int judge;
int i, j, k;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
//初始化
ans = 0;
judge = 0;
for(i=1; i<=n; i++){
d[i] = i;
}
//输入坐标
for(i=1; i<=n; i++){
scanf("%d%d",&dao[i].x,&dao[i].y);
}
//计算距离的平方
k = 1;
for(i=1; i<=n; i++){
for(j=i+1; j<=n; j++){
dist = sqrt(square(abs(dao[i].x - dao[j].x)) + square(abs(dao[i].y - dao[j].y)));
if(10 <= dist && dist <= 1000){ //选择满足条件的距离
e[k].u = i;
e[k].v = j;
e[k].len = dist;
k++;
}
}
}
k--;
//根据距离排序,注意从1开始!!
sort(e+1,e+k+1,cmp);
for(i=1; i<=k; i++){
fx = find_r(e[i].u);
fy = find_r(e[i].v);
if(fx != fy){
d[fx] = fy;
ans += e[i].len;
}
}
int r = find_r(1);
for(i=2; i<=n; i++){
if(find_r(i) != r){ //存在不相同的代表根,则不互相通达
judge = 1;
break;
}
}
if(judge || judge) printf("oh!\n");
else printf("%.1f\n",ans * 100.0); //费用
}
return 0;
}
最后,希望路过的dl们能给予改进的建议!