【题解】洛谷 CF11D A Simple Task


题目

原题链接

解题思路

A Simple Task

题面翻译

求无向图中的简单环个数,保证不存在重边和自环。

简单环:除起点外,其余的点都只出现一次的回路。

题目描述

Given a simple graph, output the number of simple cycles in it. A simple cycle is a cycle with no repeated vertices or edges.

输入格式

The first line of input contains two integers n n n and m m m ( 1 ≤ n ≤ 19 1\le n\le19 1n19 , 0 ≤ m 0\le m 0m ) – respectively the number of vertices and edges of the graph. Each of the subsequent $ m $ lines contains two integers a a a and b b b , ( 1 ≤ a , b ≤ n 1\le a,b\le n 1a,bn , a ≠ b a≠b a=b ) indicating that vertices a a a and b b b are connected by an undirected edge. There is no more than one edge connecting any pair of vertices.

输出格式

Output the number of cycles in the given graph.

样例 #1

样例输入 #1
4 6
1 2
1 3
1 4
2 3
2 4
3 4
样例输出 #1
7

提示

The example graph is a clique and contains four cycles of length 3 and three cycles of length 4.

分析

考虑用状压DP:
f [ j ] [ i ] f[j][i] f[j][i] 表示 n n n 个节点走过的状态,起点为 i i i 中最小的 1 1 1 的位置,且终点为 j j j 的路径总数。

考虑转移方程: f [ j ] [ i ] f[j][i] f[j][i] 转移至 f [ k ] [ i ∣ ( 1 < < ( k − 1 ) ) ] f[k][i|(1<<(k-1))] f[k][i(1<<(k1))],由题可得是直接累加的,但必须满足以下条件:

  1. 状态 f [ j ] [ i ] f[j][i] f[j][i] 存在。
  2. j j j 能到达 k k k
  3. k k k 大于 i i i 中最小的 1 1 1 的位置。
  4. i i i 中没有 k k k 这个位置。

那么如果已经有了 k k k 这个点且 k k k 就为起点,那就是找到环了,直接统计即可。

注意:

  1. 初始化
    f[i][1<<(i-1)]=1;

即出发的点。

  1. 转移
    同上:
	if(i&(1<<(k-1)))
	{
		if(lowbit(i)==(1<<(k-1)))
			ans+=f[j][i];
	}
	else
		f[k][i|(1<<(k-1))]+=f[j][i];
  1. 统计答案
    会有两个不合法情况, 1 1 1 是每一条单独的边会被算为答案, 2 2 2 是合法的环,其反环会被多算一遍。
    所以最终答案为 a n s − m 2 {\Large \frac{ans-m}{2}} 2ansm

Code

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL);
using namespace std;
const int N=1<<19;
int f[20][N],n,m,x,y,a[101][101],ans;
inline int lowbit(int x)
{
	return x&-x;
}
signed main()
{
	IOS;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>x>>y,a[x][y]=a[y][x]=1;
	for(int i=1;i<=n;i++)
		f[i][1<<(i-1)]=1;
	for(int i=0;i<(1<<n);i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(!f[j][i])
				continue;
			for(int k=1;k<=n;k++)
			{
				if(!a[j][k])
					continue;
				if(lowbit(i)>(1<<(k-1)))
					continue;
				if(i&(1<<(k-1)))
				{
					if(lowbit(i)==(1<<(k-1)))
						ans+=f[j][i];
				}
				else
					f[k][i|(1<<(k-1))]+=f[j][i];
			}
		}
	}
	cout<<(ans-m)/2;
	return 0;
}

更多方法

更多方法

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值