UPC 2020年夏混合个人训练第六十五场【E&F】

问题 E: 爆炸化合物 (explosion)

题目描述
各国都在加大力度防范恐怖份子对于飞机的袭击,X 国也不例外,液体一律不准带上飞机。
但是,X 国最近打探到某恐怖分子集团正在密谋策划一项恐怖袭击,恐怖分子们准备使用固体材料来制造炸弹!
幸运的是,X 国已经知道了恐怖分子们将使用某些特定的化合物来制造炸弹。每种化合物是由2种不同的不明元素构成的。如果将k个化合物放在一起,而这k个化合物恰好含有k种元素,那么这些化合物就构成了一个炸弹。
例如:现在有6个化合物A+B,G+B,D+F,A+E,E+G,F+H ,其中A+B,G+B,A+E,E+G这4种化合物中恰好含有4种元素,因此这4种化合物如果在一起,那么就会构成炸弹。
现在,机场的安检部分已经收到了若干个即将被带上飞机的化合物,这些化合物将依次上飞机,对于每个化合物,你需要判断该化合物能否上飞机(即判断他与之前已经上飞机的化合物是否会构成炸弹)。对于上面的例子,第5个化合物将不能被带上飞机。

输入
给出若干行,每行两个整数a和b,表示该化合物的两种元素。输入的最后,用一个-1表示输入结束。

输出
一行一个数,表示有多少个化合物不能上飞机。

样例输入
1 2
3 4
3 5
3 1
2 3
4 1
2 6
6 5
-1

样例输出
3

提示
对于所有数据,元素的标号是一个 [0,10^5] 中的整数,最多有10^5个化合物。

很好的图示题解

// 搜索、环
#include <cstdio>
using namespace std;

int a,b,ans,fa[100001];

int find(int x)
{
	return x==fa[x] ? fa[x]:fa[x]=find(fa[x]);
	//路径压缩,在找到根节点时,一路将沿途找到的所有点的父亲都指向根
	//三目运算,相当于:
	/*
		if (fa[x]!=x) fa[x]=find(fa[x]);
		return fa[x];  //或写成:else return x;
	*/
	//或者:
	/*
		if (x==fa[x]) return x; //x或fa[x]无所谓
		fa[x]=find(fa[x]);
	*/
}

int main()
{
	for(int i=1; i<100001; i++)  fa[i]=i; //初始化,各元素间没有联系
	while(scanf("%d",&a),a!=-1) //判断输入的数是否为-1,从而结束输入
	{
		scanf("%d",&b);
		if(find(a)==find(b))  ans++;
		//如果此次化合物所相连的元素已经在同一连通块中,则ans++;
		else  fa[find(b)]=find(a); //合并两个元素
	}
	cout<<ans;
	return 0;
}

/*
#include <iostream>
using namespace std;
int k,ans,a,b;
int v[100001];
int find(int k) {
	if(v[k]==k) return k; //已被搜索
	return v[k]=find(v[k]); //未被搜索,用v[k]存起来,使数扁平化,减少时间复杂度,以防超时
}
int main() {
	for(int i=1; i<=100000; i++) v[i]=i; //预先存好
	while(true) {
		scanf("%d",&a);
		if(a==-1) break;
		k++;
		scanf("%d",&b);
		if(find(a)==find(b)) ans++; //构成一个环,即此时化合物数量和元素数量相等,可以制造炸弹
		else v[find(b)]=find(a); //继续搜索
	}
	printf("%d",ans);
	return 0;
}
*/

问题 F: 修建道路 (roads)

题目描述
Farmer John 最近得到了一些新的农场,他想新修一些道路使得他的所有农场可以经过原有的或是新修的道路互达 (也就是说,从任一个农场都可以经过一些首尾相连道路到达剩下的所有农场)。有些农场之间原本就有道路相连。
所有N(1≤N≤1000个农场(用1…N顺次编号)在地图上都表示为坐标为(Xi,Yi)的点(0≤Xi≤1000000,0≤Yi≤1000000),两个农场间道路的长度自然就是代表它们的点之间的距离。
现在 Farmer John 也告诉了你农场间原有的M(1≤M≤1000条路分别连接了哪两个农场, 他希望你计算一下,为了使得所有农场连通,他所需建造道路的最小总长是多少。

输入
第 1行: 2个用空格隔开的整数:N和 M
第2…N+1 行:第i+1行为2个用空格隔开的整数:Xi,Yi
第N+2…N+M+1行:每行用2个以空格隔开的整数i、j描述了一条已有的道路,这条道路连接了农场i和农场j

输出
第1行:输出使所有农场连通所需建设道路的最小总长,保留2位小数,不必做任何额外的取整操作。为了避免精度误差,计算农场间距离及答案时,请使用64位实型变量。

样例输入
4 1
1 1
3 1
2 3
4 3
1 4

样例输出
4.00

提示
样例解释
FJ 一共有4个坐标分别为 (1,1),(3,1),(2,3),(4,3)的农场。农场1和农场4之间原本就有道路相连。
FJ 选择在农场1和农场2 间建一条长度为2.00的道路,在农场3和农场4间建一条长度为2.00的道路。这样,所建道路的总长为4.00,并且这是所有方案中道路总长最小的一种。
对于所有的数据,1≤N,M≤1000,0≤Xi,Yi≤1000000

题解:
最小生成树(KUSKAL)
大意:有一些已经建好的边,问你再建多长的边能够使原图有一个最小生成树。
如果先依照题意连边有点恶心。所以考虑更优秀的一种做法(其实是更巧妙)先给题目的边打标记,最后建全图,如果有标记的边我们把它的边权置成0,这样就保证它一定在最小生成树上。

// 最小生成树(KUSKAL)
#include <bits/stdc++.h>
#define ll long long
using namespace std;

struct sb {int x,y;}a[2001];

struct A{
    int from,to; 
	double val;
}e[1000001];

int mapp[1001][1001];

double calc(int x1,int x2)
{
    double ans=sqrt((double)(a[x1].x-a[x2].x)*(a[x1].x-a[x2].x)+(double)(a[x1].y-a[x2].y)*(a[x1].y-a[x2].y));
    return ans;
}

int fa[1001];
bool cmp(A a,A b){ return a.val<b.val; }
int find(int x){ return x==fa[x] ? x:fa[x]=find(fa[x]); }

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    
    for(int i=1,xx,yy; i<=n; i++) {
        scanf("%d%d",&xx,&yy);
        a[i].x=xx; a[i].y=yy;
    }
    
    int tot=0;
    for(int i=1,x,y; i<=m; i++) {
        scanf("%d%d",&x,&y);
        e[++tot].from=x; e[tot].to=y; e[tot].val=0;
        mapp[x][y]=1;
    }
    
    for(int i=1;i<n;i++)
      for (int j=i+1;j<=n;j++)
      {
           if(i==j||mapp[i][j]==1) continue;
           e[++tot].from=i; e[tot].to=j; e[tot].val=calc(i,j);
      }
      
    sort(e+1,e+1+tot,cmp);
    for(int i=1;i<=n;i++)  fa[i]=i;
    
    double ans=0;
    int cnt=0;
    for(int i=1; i<=tot&&cnt<n-1; i++)
    {
        int u=find(e[i].from),v=find(e[i].to);
        if(u==v)   continue;
        else {
        	ans+=e[i].val;
			cnt++;
			fa[u]=v;
		}
    }
    
    printf("%.2f",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米莱虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值