当前位置: 首页 > news >正文

刷题记录:牛客NC16544简单环

传送门:牛客

题目描述:

给定一张n个点m条边的无向图,求出图中所有简单环的数量。(简单环:简单环又称简单回路,图的顶点序列中,除
了第一个顶点和最后一个顶点相同外,其余顶点不重复出现的回路叫简单回路。或者说,若通路或回路不重复地包含
相同的边,则它是简单的)
输入;
4 6 3
1 2
2 3
3 4
4 1
2 4
1 3
输出:
4
3
0

一道状压dp题.但是感觉还是挺难看出来的…

主要思路;

  1. 首先我们需要先将这道题往状压dp的那条思路上去想.然后我们就会想到可以这么做,就是用一个 d p [ S ] [ u ] dp[S][u] dp[S][u]来存储状态为S时(我们将每一个点是不是在链中用二进制表示形成一个集合S)终点为 u u u的链的数量(注意此时并不是环的数量).
  2. 为了不漏不重复,我们可以将每一个状态的第一个1当做我们的起点位置 k k k,将最后的 u u u当做我们的终点位置,然后对于我们的这条链,显然我们可以用 u u u的邻接点 v v v来进行转移,这里的转移依据是,既然我们的开始位置 k k k到结束位置 u u u能够组成一条链,那么我们的 k k k v v v显然也是可以组成一条链的,那么我们就可以进行转移了.

d p [ S ∣ ( 1 < < v ) ] [ v ] = ( d p [ S ] [ u ] + d p [ S ∣ ( 1 < < v ) ] [ v ] ) dp[S|(1<<v)][v]=(dp[S][u]+dp[S|(1<<v)][v])%mod dp[S(1<<v)][v]=(dp[S][u]+dp[S(1<<v)][v])

注意此时需要累加,而不是直接进行转移,为什么呢,因为我们的这条链只是确定了起点和末尾所以我们的这条链并不是确定的,所以我们的需要进行的是累加.

  1. 然后对于我们枚举的每一个状态(也就是每一条链),因为我们记录了链的开头和结尾,所以直接判断一下开头和结尾能不能链接即可(也就是这条链能不能组成一个环)

在下列代码中用到了一个比较偏的c++函数

_ _ b u i l t i n _ p o p c o u n t ( ) \_\_builtin\_popcount() __builtin_popcount(),这个库函数是用来求出一个数的二进制位中1的数量(复杂度应该是 O ( 1 ) O(1) O(1)的,使用查表法做的)

  1. 然后是一些比较琐细的问题,就是在之前的求的过程中,即使我们安排了顺序但是还是有重复的,因为对于一个环来说,即使我们确定了开头,但是由于顺逆时针的关系,会导致我们最后的答案是两倍的,所以我们需要除二,但是由于我们过程中用到了取模,所以我们并不能直接除2,所以需要乘法逆元,对于乘法逆元,如果不清楚的可以看我的这篇博客,对乘法进行了较为详细的解释和讲解

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define int long long 
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
const int mod=998244353;
int n,m,k;
ll qpow(ll a,ll b) {
	ll ans=1;
	while(b) {
		if(b&1) ans=(ans*a)%mod;
		b>>=1;
		a=(a*a)%mod;
	}
	return ans;
}
int mp[30][30],dp[1<<21][30];int res[30];
signed main() {
	n=read();m=read();k=read();
	int u,v;
	for(int i=1;i<=m;i++) {
		u=read();v=read();
		mp[--u][--v]=1;mp[v][u]=1;
	}
	for(int i=0;i<n;i++) dp[1<<i][i]=1;
	for(int S=1;S<(1<<n);S++) {
		int start=-1;
		for(int j=0;j<n;j++) {
			if((S>>j)&1) {
				start=j;
				break;
			}
		}
		for(int u=0;u<n;u++) {
			if(!((S>>u)&1)) continue;
			if(dp[S][u]==0) continue;
			for(int v=start+1;v<n;v++) {
				if((S>>v)&1) continue;
				if(!mp[u][v]) continue;
				dp[S|(1<<v)][v]=(dp[S][u]+dp[S|(1<<v)][v])%mod;
			}
			if(mp[u][start]) {
				if(__builtin_popcount(S)<3) continue;
				res[__builtin_popcount(S)%k]=(res[__builtin_popcount(S)%k]+dp[S][u])%mod;
			}
		}
	}
	for(int i=0;i<k;i++) {
		cout<<(res[i]*qpow(2,mod-2))%mod<<endl;
	}
	return 0;
}

相关文章:

  • 初学python+QT做GUI(零基础)
  • AlertDialog6种使用方法
  • Java+MySQL基于SSM的二手玩具交换网站
  • 哈啰出行高质量故障复盘法:“3+5+3”(附模板)
  • 为什么企业传统网络访问海外应用程序不稳定、速度慢?怎么解决?
  • 【OpenFeign】【源码+图解】【四】FeignClient实例工具类ReflectiveFeign
  • springboot 定时任务基础模板
  • zabbix添加一个ubuntu受监控主机
  • Android8.1下拉状态栏菜单和系统设置添加触摸开关功能
  • Java HashSet
  • 青少年等级考试【Python通关干货知识点】(一级)
  • MongoDB在Java中的使用
  • 简单阐述对称加密算法和非对称加密算法(附C++示例代码,以openssl实现AES、DES、RSA、ECC、DSA算法加密)
  • 理解操作系统(Linux)
  • django logging的StreamHandler的一个小用法
  • IJCAI-2022 多级发射方法的脉冲神经网络
  • Spring中自定义事件监听
  • 工具(二):Nginx 扩展 OpenResty
  • css:隐藏input file标签并触发点击上传文件事件
  • EMQX Cloud 自定义函数实现多种 IoT 数据形式的灵活转化