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>=Si,j
如 果 j 在 S 中 且 j = = S 总 编 号 最 小 的 ( 即 起 点 ) , 说 明 找 到 了 环 − − > a n s + = f [ S ] [ i ] 如果j在S中且j==S总编号最小的(即起点),说明找到了环--> ans += f[S][i] jSj==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<<(j1)][j]+=f[S][i]
然后就没了QWQ
对了,这样的话每个环会被算两次,然后还有只有一条边的情况也会被算进去
所以最后的答案为 ( a n s − m ) / 2 (ans-m)/2 (ansm)/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
好像很多状压题(算方案数的)都可以钦定关键点哎
钦定大法好

第150篇blog!!!

posted @ 2019-12-12 20:51  lahlah  阅读(24)  评论(0编辑  收藏  举报