cf11 D. A Simple Task
cf11 D. A Simple Task
题意:
求简单图(无向、无重边、无自环)中简单环(不重复经过点/边)的数量
思路:
能不能暴力嗯dp? 表示起点为 ,终点为 的方案数。这样会把非简单环算进去。
考虑状压, 表示路径上的所有点组成点集 ,起点是 中编号最小的点(记为 )(思想类似 lowbit),终点是 的方案数。
由 扩展到点 :
-
如果 比 还小,忽略之
-
对于 ,如果 就是 ,说明找到了一个环,更新答案;否则就走到了路径上走过的点,不是简单环了,忽略之
-
对于 ,更新
更新的顺序:从小到大枚举所有可能的 就好了。这样循环到 之前, 一定被所有能扩展到 的状态扩展过
注意每个简单环被算了两次(顺时针和逆时针);另外每个 的只有两条边的环也是非法的,这种有 个
所以输出
当然也可以用 __builtin_popcount
算 中的点数,当环中点数大于2时才更新ans,这样最后只需除以2,不用减m
const signed N = 19; //开到21会MLE
int n, m; bool g[N][N];
ll f[1<<N][N], ans;
int get(int S) { //非空集S的起点
for(int i = 0; ; i++)
if(S>>i&1) return i;
}
bool in(int S, int x) { //u是否在集合S中
return (S>>x&1);
}
signed main() {
iofast;
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
u--, v--; //二进制,-1比较方便
g[u][v] = g[v][u] = 1;
}
for(int i = 0; i < n; i++) f[1<<i][i] = 1; //初始化
for(int S = 1; S < (1<<n); S++) {
int start = get(S);
for(int i = 0; i < n; i++) {
if(!f[S][i]) continue; //不存在的状态
if(g[i][start]) ans += f[S][i];
for(int j = start+1; j < n; j++)
if(g[i][j] && !in(S,j))
f[S|(1<<j)][j] += f[S][i];
}
}
cout << (ans-m)/2;
}
标签:
状压
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通