洛谷P1656 炸铁路题解

本蒟蒻的第一篇题解

原题链接

呃呃呃,先简化题意,大概的意思就是说,让你把m条铁路的其中一条铁路炸掉,使得这n个城市中有两个城市不连通(用并查集的思想就是让你把一条边给抠掉,使得有至少两个连通块)。

如果不看标签,很快就可以想到一种思路:

先输入m条路径,每条路径都会有两个不同的城市,用桶的思想统计每个城市出现的次数,如果某个城市只出现了一次,那么那个城市所在的那一条铁路就是key road

于是便兴致勃勃地打出了代码:

#include<bits/stdc++.h>
using namespace std;
#define SF scanf
#define PF printf
int f[155], u[5005], v[5005];//f数组是桶,记录每个城市出现的次数,u数组是起点,v数组是终点
int main() {
	int n, m;
	SF("%d%d", &n, &m);
	for(int i = 1; i <= m; i++) {
		SF("%d%d", &u[i], &v[i]);
		f[u[i]]++;//统计城市出现次数
		f[v[i]]++;//同上
	}
	for(int i = 1; i <= n; i++) {
		if(f[i] == 1) {//如果某个城市只出现了一次
			PF("%d %d\n", u[i], v[i]);//输出他所在的路径
		}
	}
	return 0;
} 

交一下试试?

听取WA声一片

呃呃呃,这可是道黄题啊,能让你这么轻松地水过去?

为什么会WA呢?

因为那个并查集的思想,是让你搞两个连通块出来,又不是让你去隔离某个城市

所以我们要换个思路?

没错,就是克鲁斯卡尔最小生成树(别问我为什么不写并查集,问就是懒得写,套模板不香吗)

问题又来了,怎么去巧妙地改一下这个模板呢?

很简单,我们把两个城市之间的路径设为1,就代表没有被炸;设为INT_MAX,就代表被炸了

like this:

for(int i = 1; i <= m; i++) {
	int u, v;
	SF("%d%d", &u, &v);
	s[i].u = u;
	s[i].v = v;
	s[i].w = 1;//刚开始时铁路都没有被炸
}

之后去一个个枚举炸哪个铁路

就像这样:

for(int i = 1; i <= m; i++) {
	s[i].w = INT_MAX;//将第i个铁路炸掉
	KAL(i);//模板
	s[i].w = 1;//记得回溯哦~
}

那么KAL里面又该怎么改呢?

我们知道,只要两个城市不在一个连通块里面,那么就可以选择这两个城市之间的路径进行连通。我们又知道,当两个城市之间的路径不为INT_MAX时,也可以选择进行连通,那么我们就可以得到以下的改了一点点的模板代码:

void KAL(int q) {
	for(int i = 1; i <= n; i++) {//初始化并查集
		fa[i] = i;
	}
	int sum = 0;
	for(int i = 1; i <= m; i++) {//遍历每一条边
		int fu = fun(s[i].u);
		int fv = fun(s[i].v);
		if(fu != fv && s[i].w != INT_MAX) {//上面的判断条件
			sum++;
			fa[fu] = fv;//合并并查集
			if(sum == n - 1) return;//如果连通的边数是n-1,则代表一定是且仅是一个连通块
		}
	}
	PF("%d %d\n", s[q].u, s[q].v);//如果遍历完所有边后还未连通,则说明至少有两个连通块,直接输出
}

再次开开心心的提交

挺无语的

再次陷入沉思……

要不再去题目中看看?

请注意:输出时,所有的数对 <a,b>必须按照 a 从小到大排序输出;如果a 相同,则根据 b 从小到大排序。

又没有认真读题吧

咳咳,问题不大,只需要我们答案先储存到数组里,然后写个cmp,最后再统一输出就行了

ans[++r].u = s[q].u;//储存答案
ans[r].v = s[q].v;
bool comp(node a, node b) {//快乐的cmp函数
	return a.u == b.u ? a.v < b.v : a.u < b.u;
}
sort(ans + 1, ans + 1 + r, comp);//排序
for(int i = 1; i <= r; i++) {//统一地输出
	PF("%d %d\n", ans[i].u, ans[i].v);
}

又再次开开心心地提交

再次无语

为什么呢?

最后,我实在想不出来了,去下载了一个数据,发现a一定要比b小

于是,我重振旗鼓,再次稍微修改了一下:

ans[++r].u = min(s[q].u, s[q].v);//起点取小的
ans[r].v = max(s[q].u, s[q].v);//终点取大的

提交后

终于过了哈哈哈

5ms,不知道是数据水还是代码跑的快

就这样,你就AC了一道黄题

真实的做题历程

其实这样一点点地把一道题做对的过程挺好的(开始煽情呜呜呜)

OK,see you,bye!

欸,好像少了点什么

哦,我懂了

代码时刻

#include<bits/stdc++.h>
using namespace std;
#define SF scanf
#define PF printf
struct node {
	int u, v, w;
}s[10005], ans[10005];
int fa[155];
int fun(int x) {
	if(fa[x] == x) return x;
	return fa[x] = fun(fa[x]);
}
int n, m, r;
bool cmp(node a, node b) {
	return a.u == b.u ? a.v < b.v : a.u < b.u;
}
void KAL(int q) {
	for(int i = 1; i <= n; i++) {
		fa[i] = i;
	}
	int sum = 0;
	for(int i = 1; i <= m; i++) {
		int fu = fun(s[i].u);
		int fv = fun(s[i].v);
		if(fu != fv && s[i].w != INT_MAX) {
			sum++;
			fa[fu] = fv;
			if(sum == n - 1) return;
		}
	}
	ans[++r].u = min(s[q].u, s[q].v);
	ans[r].v = max(s[q].u, s[q].v);
}
bool comp(node a, node b) {
	return a.u == b.u ? a.v < b.v : a.u < b.u;
}
int main() {
	SF("%d%d", &n, &m);
	for(int i = 1; i <= m; i++) {
		int u, v;
		SF("%d%d", &u, &v);
		s[i].u = u;
		s[i].v = v;
		s[i].w = 1;
	}
	sort(s + 1, s + 1 + m, cmp);
	for(int i = 1; i <= m; i++) {
		s[i].w = INT_MAX;
		KAL(i);
		s[i].w = 1;
	}
	sort(ans + 1, ans + 1 + r, comp);
	for(int i = 1; i <= r; i++) {
		PF("%d %d\n", ans[i].u, ans[i].v);
	}
	return 0;
}

拒绝复制,从我做起

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值