Description
从前有个包含n个点,m条边,无自环和重边的无向图。
对于两个没有直接连边的点u;v,你可以将它们合并。具体来说,你可以删除u;v及所有以它们作为端点的边,然后加入一个新点x,将它与所有在原图中与u或v有直接连边的点连边。
你需要判断是否能通过若干次合并操作使得原图成为一条链,如果能,你还需要求出这条链的最大长度
Input
从文件merge.in中读入数据。
第一行两个正整数n;m,表示图的点数和边数。
接下来m行,每行两个正整数u;v,表示u和v之间有一条无向边
Output
输出到文件merge.out中。
如果能通过若干次合并操作使得原图成为一条链,输出链的最大长度,否则输出-1
Sample Input
【样例1输入】
5 4
1 2
2 3
3 4
3 5
【样例2输入】
4 6
1 2
2 3
1 3
3 4
2 4
1 4
Sample Output
【样例1输出】
3
【样例2输出】
-1
Data Constraint
对于30%的数据, n<=10
对于70%的数据, m<=2000
对于100%的数据, n<=1000,m<=10^5
思路
首先考虑一个联通块
若这个联通块存在奇环,它就无法变成一条链。(画一下图就知道啦)
若这个图合法的话,就是一个二分图
先将二分图中的全部联通分量和每个点属于哪个联通分量求出来
这个可以用dfs实现
对于每一个连通分量都构造了一条链
而对于任意两条链
显然可以通过一 次合并操作将它们并成一条,长度为它们的长度之和
因此,答案就是所有连通块的直径之和
可以用spfa实现
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100077,inf=0x3f3f3f3f;
int n,m,cnt,cntt,dis[maxn],d[maxn],co[maxn],list[maxn],f[maxn],ans;
bool bb,b[maxn];
struct E
{
int to,next;
}e[maxn*2];
void add(int u,int v)
{
e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt;
}
void dfs(int u,int c,int t)
{
if(bb) return;
f[u]=t; co[u]=c;
for(int i=list[u]; i; i=e[i].next)
{
if(bb) return;
int v=e[i].to;
if(!co[v])
{
dfs(v,-c,t);
if(bb) return;
}else
if(co[u]+co[v])
{
bb=1; return;
}
}
}
int spfa(int s)
{
for(int i=1; i<=n; i++) d[i]=inf;
queue<int> q;
q.push(s); d[s]=0; b[s]=1;
int mx=0;
while(!q.empty())
{
int u=q.front(); q.pop();
mx=max(mx,d[u]);
for(int i=list[u]; i; i=e[i].next)
{
int v=e[i].to;
if(d[v]>d[u]+1)
{
d[v]=d[u]+1;
if(!b[v]) q.push(v),b[v]=1;
}
}
b[u]=0;
}
return mx;
}
int main()
{
freopen("merge.in","r",stdin); freopen("merge.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
for(int i=1; i<=n; i++) if(!co[i])
{
dfs(i,1,++cntt);
if(bb)
{
printf("-1"); return 0;
}
}
for(int i=1; i<=n; i++) dis[f[i]]=max(dis[f[i]],spfa(i));
for(int i=1; i<=cntt; i++) ans+=dis[i];
printf("%d",ans);
}