原文链接https://www.cnblogs.com/zhouzhendong/p/CF542E.html
题目传送门 - CF542E
题目传送门 - 51Nod1481
题意
有一幅无向图,它有n个点,m条边,没有自环和重边。定义合并操作,这个合并操作是把两个没有边直接连接的点合并成一个新点,把和旧的两个点至少有一个有边的点和这个新点连边。然后原来的两个旧点删除。这样就把n个点的无向图变成了n-1个点的无向图。
现在,要求你对这个图进行合并操作,最后形成一条链,而且这个链要尽可能的长。一条长度为k的链(k ≥ 0)是由k+1个点和k条边构成的,而且第i个点和第(i+1)个点要有一条边相连(1 ≤ i ≤ k)。特别的,只有一个点的图,是一条长度为0的链。经过合并出来的新点可以再次参加合并操作。
上图解释了一次合并操作,被合并的两个点用红色标出了。
题解
首先,如果原图存在奇环,那么必然是 -1 ,因为合并到最后会变成一个无法合并的三元环。
否则,图就是一个二分图。
考虑对于每一个连通块分别求解。
对于一个连通块的任意一个 bfs 生成树,必然可以把它缩成一条长度为 bfs 深度的链。于是我们对于每一个连通块枚举一下起点就好了。
最终答案就是所有连通块的答案相加。
时间复杂度 $O(nm)$ 。
代码
#include <bits/stdc++.h> using namespace std; int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=1005; int n,m; vector <int> e[N],s; int c[N],vis[N]; int dfs(int x,int d){ if (~c[x]) return c[x]==d; s.push_back(x); c[x]=d; for (auto y : e[x]) if (!dfs(y,d^1)) return 0; return 1; } int q[N],dis[N],head,tail; int dfs2(int s){ memset(vis,0,sizeof vis); head=tail=0; q[++tail]=s,dis[s]=0,vis[s]=1; while (head<tail){ int x=q[++head]; for (auto y : e[x]) if (!vis[y]) vis[y]=1,dis[y]=dis[x]+1,q[++tail]=y; } return dis[q[tail]]; } int main(){ n=read(),m=read(); for (int i=1;i<=m;i++){ int a=read(),b=read(); e[a].push_back(b); e[b].push_back(a); } memset(c,-1,sizeof c); int ans=0; for (int i=1;i<=n;i++) if (!~c[i]){ s.clear(); if (!dfs(i,0)) return puts("-1"),0; int now=0; for (auto S : s) now=max(now,dfs2(S)); ans+=now; } printf("%d",ans); return 0; }