2021蓝桥杯省赛C++A组试题E 回路计数 状态压缩DP详细版
2021蓝桥杯省赛C++A组试题E 回路计数 状态压缩DP
题目描述
蓝桥学院由21栋教学楼组成,教学楼编号1到21。对于两栋教学楼a和b,当a和b互质时,a和b之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?两个访问方案不同是指存在某个i,小蓝在两个访问方法中访问完教学楼i后访问了不同的教学楼。
提示:建议使用计算机编程解决问题。
解题思路
哈密尔顿回路是经典的NP难问题,暴力搜索效率太低,到后面时间非常长。采用状态压缩DP,可以在2秒左右完成遍历求解,时间可以接受。
dp数组解释:dp[i][j]表示当前正在i结点,走过的路径为j时的方案数。
状态转移方程:dp[j][S|(1<<j)]=dp[j][S|(1<<j)]+dp[i][S]
解释:从i结点转移到j结点,当前到达j结点,S为到达i结点时所走过的结点序列(二进制表示,如10101表示走过了1、 3、 5三结点,没有走过2、 4两个结点),则dp[j][S|(1<<j)]表示到达j结点时的访问方案数量。S|(1<<j)含义:S序列中第j个二进制位一定为0,因为还没有访问第j个结点,到达j节点之后,要将第j个二进制位置一。'|'和'<<'分别为按位或和左移操作。dp[i][S]为到达i结点时的不同访问方案数。即下一个结点的访问方案数为其所有前驱结点访问方案数的和。
注意:本题结果数字很大,dp数组和结果都要使用long long类型
答案
881012367360
代码实现
(关键代码已加注释)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int n=21;
bool G[n][n];//边采用邻接矩阵存储
ll dp[n][1<<n];
void generateEdge(){//生成边
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(__gcd(i,j)==1){
G[i-1][j-1]=1;
G[j-1][i-1]=1;
}
}
}
}
int main(){
memset(dp,0,sizeof dp);
memset(G,0,sizeof G);
dp[0][1]=1;//初始在0结点时,路径为000...1,方案数为1
generateEdge();
int m=1<<n;
for(int S=1;S<m;S++){//遍历访问序列
for(int i=0;i<n;i++) if(S&(1<<i))//遍历当前访问的结点,当前访问的结点其访问的序列中该二进制位必须为1
for(int j=0;j<n;j++){//遍历下一个访问结点
if(!(S&(1<<j))&&G[i][j])//如果j结点没有访问且i和j之间有边则状态转移
dp[j][S|1<<j]+=dp[i][S];
}
}
ll ans=0;
for(int i=0;i<n;i++){
ans+=dp[i][m-1];//最终的访问序列为全部访问过,即访问序列为21个1,可以从不同的结点开始访问,遍历求和
}
cout<<ans;
return 0;
}