CF11D A Simple Task
link : https://www.luogu.com.cn/problem/CF11D
偶然间翻到这个史前巨坑
这题颇有一种钦定的味道
还是考虑状压DP
设
f
[
S
]
[
i
]
表
示
点
集
为
S
,
钦
定
起
点
为
S
中
编
号
最
小
的
,
已
经
走
到
i
的
路
径
数
设f[S][i]表示点集为S,钦定起点为S中编号最小的,已经走到i的路径数
设f[S][i]表示点集为S,钦定起点为S中编号最小的,已经走到i的路径数
然
后
枚
举
一
个
节
点
j
,
要
保
证
j
>
=
S
中
编
号
最
小
的
且
i
,
j
有
边
然后枚举一个节点j,要保证j>=S中编号最小的且i,j有边
然后枚举一个节点j,要保证j>=S中编号最小的且i,j有边
如
果
j
在
S
中
且
j
=
=
S
总
编
号
最
小
的
(
即
起
点
)
,
说
明
找
到
了
环
−
−
>
a
n
s
+
=
f
[
S
]
[
i
]
如果j在S中且j==S总编号最小的(即起点),说明找到了环--> ans += f[S][i]
如果j在S中且j==S总编号最小的(即起点),说明找到了环−−>ans+=f[S][i]
不
然
就
走
f
[
S
∣
(
1
<
<
(
j
−
1
)
]
[
j
]
+
=
f
[
S
]
[
i
]
不然就走f[S|(1<<(j-1)][j]+=f[S][i]
不然就走f[S∣(1<<(j−1)][j]+=f[S][i]
然后就没了QWQ
对了,这样的话每个环会被算两次,然后还有只有一条边的情况也会被算进去
所以最后的答案为
(
a
n
s
−
m
)
/
2
(ans-m)/2
(ans−m)/2
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m, ans, g[55][55], f[1 << 20][22];
signed main() {
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= m; i ++) {
int u, v;
scanf("%lld%lld", &u, &v);
g[u][v] = g[v][u] = 1;
}
for(int i = 1; i <= n; i ++) f[(1 << (i - 1))][i] = 1;
for(int S = 0; S < (1 << n); S ++) {
for(int i = 1; i <= n; i ++) if((1 << (i - 1)) & S) {
int st = (S & (-S));
for(int j = 1; j <= n; j ++) if(g[i][j]) {
if((1 << (j - 1)) < st) continue;
if(S & (1 << (j - 1))) {
if((1 << (j - 1)) == st) ans += f[S][i];
} else f[S | (1 << (j - 1))][j] += f[S][i];
}
}
}
printf("%lld", (ans - m) / 2);
return 0;
}
坑点
long long!!!
QWQ
好像很多状压题(算方案数的)都可以钦定关键点哎
钦定大法好