蓝桥杯.回路计数(状压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组省赛历年真题题解 

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

  • 35
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UmVfX1BvaW50

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值