1232题目:点击打开链接
这题用到了并查集,之前在学最小生成树kruscal的时候简单了解过并查集。
并查集者,并也,查也,统统归于集合也。即其是关于集合的操作——并,查。所以并查集的关键也就是对集合的合并与对元素的查找,查找某一元素属于哪个集合。
下面是百科里面扒的c++代码,个人觉得不难理解。
//判断两点是否在同一个连通分量中
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int father[50002],a,b,m,n,p;
int find(int x)
{
if (father[x]!=x) //满足father[x]==x的x就是根节点
father[x]=find(father[x]);
return father[x];
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for (int i=1;i<=n;i++)
father[i]=i;//初始化,表示每个节点自成一个连通分量
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
a=find(a),b=find(b);father[a]=b;
}
for(int i=1;i<=p;i++)
{
scanf("%d%d",&a,&b);
a=find(a);b=find(b);
if(a==b)printf("Yes");else printf("No");
}
return 0;
}
下面是合并两个连通分量的代码:
void merge(int x,int y)
{
int fx;
int fy;
fx=find(x);
fy=find(y);
if(flag[fx]!=fy)
flag[fx]=fy;
}
上面写的是最普通的并查集,更多的可以戳---> 点击打开链接 这个写的挺明白的。
这一题实际上就是用到最简单的并查集:
最后计算有几个连通分量,要将所有点都连通,就是把连通分量减少到只剩1个,所以需要增加的数目就是现有连通分量数减一
#include <stdio.h>
#include <string.h>
int father[1010];
int n, m;
void init()
{
int i;
for(i = 1; i <= n; i++)
father[i] = i;
}
int find(int x)
{
while(x != father[x])
x = father[x];//找到father[x] == x的点就是根节点
return x;
}
void combine(int a, int b)
{
int ra = find(a);
int rb = find(b);
if(ra != rb)
father[ra] = rb;//将ra变成tb的子树
}
int main (void)
{
while(scanf("%d", &n) != EOF)
{
if(n == 0)
break;
scanf("%d", &m);
init();
int i, a, b, j;
for(i = 0; i < m; i++)
{
scanf("%d %d", &a, &b);
combine(a, b);
}
int count = 0;
for(i = 1; i <= n; i++)
{
//printf("%d ", father[i]);
if(father[i] == i)
count ++;
}
//printf("\n");
printf("%d\n", count - 1);
}
return 0;
}
1233题目:点击打开链接
单纯的最小生成树问题。
#include <stdio.h>
#include <string.h>
int map[110][110];
int n, ans;
void init()
{
int i, j;
for(i = 0; i <= n; i++)
{
for(j = 0; j <= n; j++)
map[i][j] = 999999;
}
}
void prim()
{
int i, j, dis[110];
bool vis[110];
memset(vis, 0, sizeof(vis));
for(i = 1; i <= n; i++)
dis[i] = map[1][i];
vis[1] = 1;
dis[1] = 0;
for(i = 1; i < n; i++)
{
int min = 999999, k;
for(j = 1; j <= n; j++)
{
if(!vis[j] && min > dis[j])
{
min = dis[j];
k = j;
}
}
vis[k] = 1;
ans += min;
for(j = 1; j <= n; j ++)
{
if(!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j];
}
}
}
int main(void)
{
while(scanf("%d", &n) != EOF)
{
if(n == 0)
break;
init();
ans = 0;
int m = n * (n - 1) / 2;
int i, a, b, x;
for(i = 0; i < m; i++)
{
scanf("%d %d %d", &a, &b, &x);
if(map[a][b] > x)
map[a][b] = map[b][a] = x;
}
prim();
printf("%d\n", ans);
}
return 0;
}
1863题目: 点击打开链接
这个跟上面的方法是一样的,不同的就是这题要判断不能形成最小生成树的情况。
#include <stdio.h>
#include <string.h>
int map[110][110];
int n, m, ans;
void init()
{
int i, j;
for(i = 0; i <= n; i++)
{
for(j = 0; j <= n; j++)
map[i][j] = 999999;
}
}
void prim()
{
int i, j, dis[110];
bool vis[110];
memset(vis, 0, sizeof(vis));
for(i = 1; i <= n; i++)
dis[i] = map[1][i];
vis[1] = 1;
dis[1] = 0;
for(i = 1; i < n; i++)
{
int min = 999999, k;
for(j = 1; j <= n; j++)
{
if(!vis[j] && min > dis[j])
{
min = dis[j];
k = j;
}
}
if(min == 999999)
{
ans = -1;
return ;
}
vis[k] = 1;
ans += min;
for(j = 1; j <= n; j ++)
{
if(!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j];
}
}
}
int main(void)
{
while(scanf("%d %d", &m, &n) != EOF)
{//n是村庄数,m道路数
if(m == 0)
break;
init();
ans = 0;
int i, a, b, x;
for(i = 0; i < m; i++)
{
scanf("%d %d %d", &a, &b, &x);
if(map[a][b] > x)
map[a][b] = map[b][a] = x;
}
prim();
if(ans == -1)
printf("?\n");
else
printf("%d\n", ans);
}
return 0;
}
1874题目: 点击打开链接
这题是单源最短路径问题,思路跟prim也没有差很多,不同的就是在更新数组的地方。
#include <stdio.h>
#include <string.h>
#define MAX 10000000
int map[210][210];
int dis[210];
int sta, end, n, m;
void init()
{
int i, j;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
map[i][j] = map[j][i] = MAX;
}
}
void dijkstra()
{
bool vis[210];
memset(vis, 0, sizeof(vis));
int i, j;
for(i = 0; i < n; i++)
{
dis[i] = map[sta][i];
}
dis[sta] = 0;
vis[sta] = 1;
for(i = 0; i < n; i++)
{
int min = MAX, k;
for(j = 0; j < n; j++)
{
if(dis[j] < min && !vis[j])
{
min = dis[j];
k = j;
}
}
vis[k] = 1;
for(j = 0; j < n; j++)
{
if(!vis[j] && dis[j] > min + map[k][j])//这里与prime不同
dis[j] = min + map[k][j];
}
}
}
int main (void)
{
while(scanf("%d %d", &n, &m) != EOF)
{
int i, a, b, x;
init();
//memset(map, MAX, sizeof(map));
for(i = 0; i < m; i++)
{
scanf("%d %d %d", &a, &b, &x);
if(map[a][b] > x || map[a][b] == -1)
map[a][b] = map[b][a] = x;
}
scanf("%d %d", &sta, &end);
dijkstra();
/*
for(i = 0; i < n; i++)
{
printf("%d ", dis[i]);
}
printf("\n");
*/
if(dis[end] == MAX)
printf("-1\n");
else
printf("%d\n", dis[end]);
}
return 0;
}
1875题目: 点击打开链接
还是最小生成树问题,但是这里的路径连通是有条件的,在处理的过程中,因为小于10的和大于1000的路径是不能建桥的,所以要将小于10的和大于1000的路径都看成是不存在的,不然会出错………………
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
double map[110][110];
int n;
double ans;
struct node
{
int x, y;
};
node city[110];
void init()
{
int i, j;
for(i = 0; i <= n; i++)
{
for(j = 0; j <= n; j++)
map[i][j] = 9999999;
}
}
void prim()
{
int i, j;
double dis[110];
bool vis[110];
memset(vis, 0, sizeof(vis));
for(i = 1; i <= n; i++)
{
dis[i] = map[1][i];
//printf("%lf ", dis[i]);
}
//printf("\n");
vis[1] = 1;
dis[1] = 0;
for(i = 1; i < n; i++)
{
double min = 9999999;
int k = -1;
for(j = 1; j <= n; j++)
{
if(!vis[j] && min > dis[j] && dis[j] >= 10 && dis[j] < 1000)
{
min = dis[j];
k = j;
}
}
if(min == 9999999)
{
ans = -1;
return ;
}
vis[k] = 1;
ans += min;
for(j = 1; j <= n; j ++)
{//如果dis[j]本来的距离是小于10的,而且也没有将小于10的路径初始化为最大值的话
//那么dis[j]就更新不了,结果就会错
if(!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j];
}
}
}
void count()
{
double s;
int i, j;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
if(i == j)
continue;
s = pow((double)(city[i].x - city[j].x), 2) + pow((double)(city[i].y - city[j].y), 2);
s = sqrt(s);
if(s < 10 || s > 1000)//WA了一次,就错在这里
//如果这里不这样写的话,更新数组的时候会出错
map[i][j] = map[j][i] = 9999999;
else
map[i][j] = map[j][i] = s;
}
}
}
int main(void)
{
int t, i;
scanf("%d", &t);
while(t --)
{
scanf("%d", &n);
init();
ans = 0;
for(i = 1; i <= n; i++)
{
scanf("%d %d", &city[i].x, &city[i].y);
}
count();
prim();
if(ans == -1)
printf("oh!\n");
else
{
//printf("%lf\n", ans);
ans *= 100.00000;
printf("%.1lf\n", ans);
}
}
return 0;
}
1879题目: 点击打开链接
这里不同的就是道路存在已经修了还是没有修的情况。其实也很简单,已经修了的,把修路的成本置为0,没有修的成本不变,这样在prime累加成本的时候已经修的加0,也就相当于没有花钱……就解决问题了。
#include <stdio.h>
#include <string.h>
int map[110][110];
int n, ans;
void init()
{
int i, j;
for(i = 0; i <= n; i++)
{
for(j = 0; j <= n; j++)
map[i][j] = 999999;
}
}
void prim()
{
int i, j, dis[110];
bool vis[110];
memset(vis, 0, sizeof(vis));
for(i = 1; i <= n; i++)
dis[i] = map[1][i];
vis[1] = 1;
dis[1] = 0;
for(i = 1; i < n; i++)
{
int min = 999999, k;
for(j = 1; j <= n; j++)
{
if(!vis[j] && min > dis[j])
{
min = dis[j];
k = j;
}
}
vis[k] = 1;
ans += min;
for(j = 1; j <= n; j ++)
{
if(!vis[j] && dis[j] > map[k][j])
dis[j] = map[k][j];
}
}
}
int main(void)
{
while(scanf("%d", &n) != EOF)
{
if(n == 0)
break;
init();
ans = 0;
int m = n * (n - 1) / 2;
int i, a, b, x, f;
for(i = 0; i < m; i++)
{
scanf("%d %d %d %d", &a, &b, &x, &f);
if(f == 1)//这里!!
map[a][b] = map[b][a] = 0;
else if(map[a][b] > x)
map[a][b] = map[b][a] = x;
}
prim();
printf("%d\n", ans);
}
return 0;
}