牛客挑战赛35-线图-思维+dfs

题目描述:

给定一个图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;
}

发布了144 篇原创文章 · 获赞 135 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Nothing_but_Fight/article/details/103639228