Codeforces11D(状压dp)

好题!

题意:

给你一个图,没有重边,求这个图简单环的个数;

PS :简单环:在这个环中各个边只出现一次,各个点也只出现一次。

好难想的状态。dp[st][e]  状态st 中序数最小的点记为s并且以e为终点的环的个数。

每次枚举状态时找到最小的点,然后枚举终点,在枚举中间节点,如果中间节点和起点相同那么就可以累加到总和里面。注意计算时已经重复计算了,比如说 1- 2 - 3 - 1和 1 - 3 - 2 - 1这连个环是一样的,但是重复算了,所以结果要除以2.

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
#define oo 0x3f3f3f3f
typedef long long lld;
lld dp[(1 << 19) + 1][20];
int map[20][20];

int First(int st, int n)
{
	for (int i = 0; i < n;i++)
	if (st&(1 << i))
		return i;
	return 0;
}

int main()
{
	int n, m, u, v;
	lld ans;
	while (scanf("%d %d", &n, &m) != EOF)
	{
		memset(map, 0, sizeof map);
		for (int i = 1; i <= m; i++)
		{
			scanf("%d %d", &u, &v);
			u--; v--;
			map[u][v] = map[v][u] = 1;
		}
		int Status = (1 << n) - 1;
		ans = 0;
		memset(dp, 0, sizeof dp);
		for (int i = 0; i < n; i++)
			dp[1 << i][i] = 1;
		for (int now = 0; now <= Status; now++)
		{
			int s = First(now, n);
			for (int e = s; e < n; e++)
			if ((now&(1 << e)) && dp[now][e])
			{
				for (int i = s; i < n; i++)
				if (map[e][i])
				{
					if (now&(1 << i))
					{
						if (((1 << i) | (1 << e)) == now) continue;//防止1-2 2-1 即重边
						if (s == i) ans += dp[now][e];
					}
					else
						dp[now | (1 << i)][i] += dp[now][e];
				}
			}
		}
		printf("%lld\n", ans / 2);
	}
	return 0;

}


你可能感兴趣的:(dp,codeforces)