CF574B Bear and Three Musketeers 题解

传送门:洛谷Codeforces

看到这一题,应该第一时间就想得到一个 \(\mathcal{O}({n}^{3})\) 的算法:考虑枚举这个环上的三个点,再判断这三个点是否构成环,更新答案即可。

对于答案的计算,我们可以用一个数组 \(du[i]\) 来记录每个节点的度数。而对于一个无向图中的点来说,它的邻居数量就是它的度数。

根据题意还需要去掉环中的另外两个点(题面的表述好模糊)。

综上,对于一个点 \(a\) 来说,它对它所在的环的贡献即为 \(du[a]-2\)

那么对于 \(a,b,c\) 三个点构成的环计算的答案即为 \(du[a] + du[b] + du[c] - 6\)

但是很明显会被本题 \(n \le 4\times10^{3}\) 的数据范围给卡掉。

仔细思考后不难发现能优化的只有枚举三个点的过程,在枚举的过程中产生了大量的无用情况。

那么如果我们枚举边呢。

考虑使用链式前向星或邻接表来存储整张图,枚举第一个节点(下称 \(a\)),再由 \(a\) 的所有边延伸到第二个点(下称 \(b\)),再由 \(b\) 延伸到第三个点(下称 \(c\)),这样一来就保证了 \(a\)\(b\) 之间有连边,\(b\)\(c\) 之间有连边。

这时就只需要判断 \(a\)\(c\) 是否有连边即可,而这一过程可以使用邻接矩阵来优化。

在这一顿优化后,算法就快得飞起了,很多测试点只有 \(0\) 毫秒,最慢的测试点都只有 \(77\) 毫秒,成功轰下你谷最优解。

而且吸氧不吸氧的用时居然一样

#include<iostream>
using namespace std;
const int N(4e3+5);
int n,m,ans=0x7fffffff;
int tot,h[N],nxt[N<<1],to[N<<1],du[N];
bool can[N][N];
inline void add(int x,int y){
	nxt[++tot]=h[x],to[h[x]=tot]=y;
	nxt[++tot]=h[y],to[h[y]=tot]=x;
}
int main(){
	#ifdef ytxy
	freopen("in.txt","r",stdin);
	#endif
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		du[u]++;du[v]++;
		can[u][v]=can[v][u]=1;
		add(u,v);
	}
	for(int i=1;i<=n;i++){
		for(int j=h[i];j;j=nxt[j]){
			int v1=to[j];
			for(int k=h[v1];k;k=nxt[k]){
				int v2=to[k];
				if(can[v2][i]) ans=min(ans,du[i]+du[v1]+du[v2]-6);
			}
		}
	}
	if(ans==0x7fffffff) cout<<-1;
	else cout<<ans;
}
posted @ 2022-08-13 15:27  JR_ytxy  阅读(38)  评论(0)    收藏  举报