题目描述:
给定一个图G,定义G′=L(G)为G的线图(定义稍后给出)
考虑一个图的序列,G1给定,此后Gi+1=L(Gi)(i>1)
求当n趋向于无限大的时候,∣Gn∣的值,其中∣Gn∣表示Gn所含有的点的数量。
该序列有可能发散,此时输出"-1".
线图的定义与ZJOI2018中的定义相同:
对于无向图G=⟨V,E⟩,它的线图 L(G)也是一个无向图:
它的点集大小为 E,每个点唯一对应着原图的一条边。
两个点之间有边当且仅当这两个点对应的边在原图上有公共点(注意不会有自环)。
输入描述:
第一行数字n,m表示G1的点数和边数
接下来m行,每行2个数字u,v,表示一条边
输出描述:
一行一个数字表示答案
输入样例:
3 3
1 2
2 3
1 3
输出样例:
3
核心思想:
将子图分为四种:
1、单链(也可能是个孤立点)
2、环
3、星型(如:点1作为中心点,点2、3、4作为边缘点和点1相连,边缘点只和中心点相连)
4、基于星型图,某个边缘点的度大于1。
无限次迭代线图的结果:
1、会消失,贡献为0
2、会同构,点个数不变
3、迭代一次就成环,点的个数为边缘点的个数
4、多画几个图,会发现点的个数在递增,贡献为正无穷。
代码实现:
先用for循环判断有没有图3或图4,并记录下贡献。
再用深搜判环,记录下贡献。
详见代码!
代码如下:
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e5+20;
vector<int>vec[N];
bool vis[N],book[N];
bool dfs(int x,int f)//return 1代表成环,否则是链,将标记清除
{
vis[x]=1;
int len=vec[x].size();
if(len<=1)
{
book[x]=0;
return 0;
}
for(int i=0;i<len;i++)
{
int t=vec[x][i];
if(t==f)continue;
if(book[t]) return 1;
book[t]=1;
if(!dfs(t,x)) return book[x]=0;
}
return 1;
}
int main()
{
int n,m,x,y;
cin>>n>>m;
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
vec[x].push_back(y);
vec[y].push_back(x);
}
int sum=0;//贡献
for(int i=1;i<=n;i++)
{
if(vec[i].size()>2)//图三出现
{
vis[i]=1;
int len=vec[i].size();
for(int j=0;j<len;j++)
{
vis[vec[i][j]]=1;
if(vec[vec[i][j]].size()>1)//图四出现
{
printf("-1\n");
return 0;
}
}
sum+=len;
}
}
for(int i=1;i<=n;i++)
{
if(vis[i]||book[i])continue;//剪枝
book[i]=1;
dfs(i,-1);
}
for(int i=1;i<=n;i++)
sum+=book[i];
cout<<sum<<endl;
return 0;
}