我是靠谱客的博主 害怕冬瓜,最近开发中收集的这篇文章主要介绍蓝桥杯.回路计数(状压DP),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Question:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAV29vZGVubWFu5p2c,size_20,color_FFFFFF,t_70,g_se,x_16

Result:881012367360

21x21的图答案爆int也是够麻的

Solve:

图的遍历复杂度是21的阶乘,赛时不可能跑完,考虑DP

首先

21个点,用21位的二进制01串状压表示每一栋楼是否访问

所以现在的问题就转化成了

如何将一个含有1位1,20位0的二进制数变的21位都是1,这个就有点像19年的《糖果》了

DP的基本思路:

从初始状态开始,不断的去尝试下一个能到达的楼,并且将楼加入状态中(对应的楼的二进制位变为1),那么如果21位的二进制所有数都变为了1,就说明这是一种可行的路径,将这个路径计数到dp[ (1<<21) - 1][路径的末楼下标]

之后状态转移方程

dp[ i ][ j ]表示从状态 i 到 j 的路径数

dp[ i + (1 << k) ][ k ] += dp[ i ][ j ];

原状态 i 在楼 j 已经访问过的情况下去尝试将楼 k 加入已访问的点,如果可以,则新的状态会以k为终点,其路径数为原来的值加上dp[ i ][ j ]

 某个楼x是否已经访问过的判断:

i >> x & 1 (状态 i 按位右移x位,之后取余) (判断状态 i 二进制数的第 x 位是否为1)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool v[25][25]; 
ll dp[1<<21][25];
inline int gcd(int a, int b){
	return b==0 ? a : gcd(b, a%b);
}
int main(void)
{
	ll res = 0;
	//存边,注意下标 
	for(int i = 1;i <= 21; i++){
		for(int j = 1; j <= 21; j++){
		if(gcd(i, j) == 1) 
			v[i-1][j-1] = v[j-1][i-1] = true;
		else
			v[i-1][j-1] = v[j-1][i-1] = false;
	}
	}
	dp[1][0] = 1;
	for(int i = 1; i < (1<<21); i++){
	for(int j = 0; j < 21; j++){
		
		//如果当前状态中不存在楼j,跳过 
		if(!(i>>j&1)) continue;
		            
		//寻找从楼j能够到达的下一栋楼
		for(int k = 0; k < 21; k++){
			
			//楼k已经访问或者j到k无边,跳过 
			if((i>>k&1) || !v[j][k]) continue;
			
			dp[i+(1<<k)][k] += dp[i][j];
		}
	}
	}
	
	//将以i为结尾点的回路求和 
	for(int i = 0; i < 21; i++) 
		res += dp[(1<<21)-1][i];
	cout <<res;
	return 0;
} 

最后附上蓝桥杯汇总链接:蓝桥杯C/C++A组省赛历年真题题解 

声明:图片均来源于蓝桥杯官网,以个人刷题整理为目的,如若侵权,请联系删除~

最后

以上就是害怕冬瓜为你收集整理的蓝桥杯.回路计数(状压DP)的全部内容,希望文章能够帮你解决蓝桥杯.回路计数(状压DP)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部