Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
姥姥的,这几天刷蓝桥杯的真题越刷越自闭,都快被整的怀疑人生了。现如今怎么蓝桥杯都这么难了!今天又试着做一道填空压轴题,在天降神灵(百度)的帮助下,终于理解了这道题,现就写下我自己的理解。
题目描述
蓝桥学院由 21 栋教学楼组成,教学楼编号 1 到 21。
对于两栋教学楼 a 和 b,当 a 和 b 互质时,a 和 b 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),
请问他有多少种不同的访问方案?两个访问方案不同是指存在某个 i,小蓝在两个访问方法中访问完教学楼 i 后访问了不同的教学楼。
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。 本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
思路详解
这题乍一看,这不明显一个图给你摆这了吗!二话不多说先建图:
vector<int> graph[22];
...
for(int i=1;i<=21;i++){
for(int j=i+1;j<=21;j++){
if(gcd(j,i)==1){
graph[i].pb(j);
graph[j].pb(i);
}
}
}
其中有个知识点是互质怎么求?互质指的是两个数的最小公约数为1.典型的gcd,辗转相除法即可:
int gcd(int a,int b){
if(a<b)return gcd(b,a);
if(b==0)return a;
return gcd(b,a%b);
}
(加上a<b的判断是为了保险)
然后,有些同学可能就杵这了,放在键盘上的手不知所措(这其中就包括我呜呜呜)。一般的图论,求解的往往是最短路径、最大流之类的最值问题,往往有一个最优解;但这道题求的是方案数,这样的话什么dfs呀,dijkstra呀,dicnic呀全部木大,而是又要祭出——状压dp大法。
为什么想到状压?因为只有21个点,每个点只经过一遍,从而一个21位的二进制数即可表示全部状态。但是有了状态,我们要怎么转移?知道了一个状态i,怎么把这个状态与其他状态建立联系?我们考虑第二维,j,是最后到达的点。
从而,我们的dp是这样的:dp(i,j)中的i是走过的点的状态,j是到达的最后一个点,dp值是方案数。
那么如何转移状态?换句话说,dp(i,j)所代表的状态从何而来?其实很简单。j是最后一个点,那么是从那个点走到j点的呢?遍历所有的可能性,然后全部加起来即可。
上代码
#include<bits/stdc++.h>
#define pb push_back
#define pp pop_back
using namespace std;
using ll=long long;
vector<int> graph[22];
int gcd(int a,int b){
if(a<b)return gcd(b,a);
if(b==0)return a;
return gcd(b,a%b);
}
ll dp[1<<21][22];
int main(){
for(int i=1;i<=21;i++){
for(int j=i+1;j<=21;j++){
if(gcd(j,i)==1){
graph[i].pb(j);
graph[j].pb(i);
}
}
}
dp[1][1]=1;
for(int i=1;i<=(1<<21)-1;i++){
for(int j=1;j<=21;j++){
if((i>>(j-1))&1){
ll lasti=i^(1<<j-1);
for(auto k:graph[j]){
dp[i][j]+=dp[lasti][k];
}
}
}
}
ll ans=0;
for(int i=1;i<=21;i++){
ans+=dp[(1<<21)-1][i];
}
cout<<ans;
}
总结
这次尝试了一些不一样的风格,要是一直按照以前的方式写题解怕是最后要无聊死。还是应该把写题解变得轻松一点才好呀捏。