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;
}

浙公网安备 33010602011771号