更快的哈密顿路径/哈密顿回路算法
对于哈密顿问题,朴素的做法通常是 或者 的简单状压。接下来我们对哈密顿问题的状压做法进行若干优化,做到空间 ,时间 。
首先朴素状压设 表示是否存在一条 到 的经过的点集为 的路径。
01 状态显然非常浪费,可以拿类似 bitset 的技巧优化一下。我们将 这一维压成一个数,状态变成 ,对于点 的邻域也压成一个数 ,那么转移的判断条件变成了
时间优化到了 ,空间到了 。
接下来有两种优化路线:
Route 1
给出这道题,此题瓶颈在于判断每一个点的集合是否是一个回路。
回路的特点是起点任意,所以我们钦定集合中最小的那个点永远是起点,扔掉了 这一维,接下来按照上述方法优化转移即可。
输出方案需要一定的技巧,如果直接记录每个状态的前驱空间复杂度会多一个 ,这样常数巨大。
正确的做法是用 和 操作每次找出一个当前点的合法前驱。
原题代码:
#include <bits/stdc++.h> #define fi first #define se second using namespace std; typedef long long ll; typedef pair<int,int> pii; template<typename T=int> T read(){ char c=getchar();T x=0; while(c<48||c>57) c=getchar(); do x=(x<<1)+(x<<3)+(c^48),c=getchar(); while(c>=48&&c<=57); return x; } int n,m; int g[20]; int f[1<<20]; bool cir[1<<20]; int res[20]; int lis[20],rk; void output(int s,int x){ puts("Yes"); for(int i=0;i<n;++i){ if(s>>i&1) continue; res[i]=__builtin_ctz(g[i]&s); } while(s^=(1<<x)){ lis[rk++]=x; x=__builtin_ctz(f[s]&g[x]); } lis[rk++]=x; for(int i=1;i<rk;++i) res[lis[i-1]]=lis[i]; res[lis[rk-1]]=lis[0]; for(int i=0;i<n;++i) printf("%d ",res[i]+1); putchar('\n'); } int main(){ n=read();m=read(); for(int i=0;i<m;++i){ int u=read()-1,v=read()-1; g[u]|=(1<<v); g[v]|=(1<<u); } for(int i=0;i<n;++i) f[1<<i]=1<<i; for(int s=1;s<(1<<n);++s){ int lb=__builtin_ctz(s); for(int i=lb+1;i<n;++i){ if(s>>i&1) continue; if(f[s]&g[i]) f[s|(1<<i)]|=(1<<i); } if(f[s]&g[lb]){ bool fl=1; for(int i=0;i<n;++i){ if(s>>i&1) continue; if(g[i]&s) continue; fl=0;break; } if(fl){output(s,__builtin_ctz(f[s]&g[lb]));return 0;} } } puts("No"); return 0; }
哈密顿回路核心代码:
#include <cstdio> using namespace std; int n; int f[1<<24],g[24]; int lis[24],rk; char str[30]; int main(){ scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%s",str); for(int j=0;j<n;++j) g[i]|=(str[j]^48)<<j; } for(int i=0;i<n;++i) f[1<<i]=1<<i; for(int s=1;s<(1<<n);++s){ int lb=__builtin_ctz(s); for(int i=lb+1;i<n;++i){ if(s>>i&1) continue; if(f[s]&g[i]) f[s|(1<<i)]|=(1<<i); } } if(f[(1<<n)-1]&g[0]){ puts("Yes"); int s=(1<<n)-1; int x=__builtin_ctz(f[s]&g[0]); while(s^=(1<<x)){ printf("%d ",x); x=__builtin_ctz(f[s]&g[x]); } printf("%d ",x); putchar('\n'); } else puts("No"); return 0; }
上述方法不好做哈密顿路径。下面的方法更具有普遍性。
Route 2
给出这道题。
我们发现对于每个起点跑一遍 的 很浪费。我们只对一个节点 跑出以它为起点的路径信息 。
对于一条哈密顿回路,必须有 ,并且
于是枚举 就可以利用位运算统计出所有点对间是否存在哈密顿路径,自然也判断出了哈密顿回路。
原题代码:
#include <cstdio> using namespace std; int n; int f[1<<24],g[24]; int lis[24],rk; char str[30]; int res[24]; int main(){ scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%s",str); for(int j=0;j<n;++j) g[i]|=(str[j]^48)<<j; } f[1]=1; for(int s=1;s<(1<<n);s+=2){ if(!f[s]) continue; for(int i=1;i<n;++i){ if(s>>i&1) continue; if(f[s]&g[i]) f[s|(1<<i)]|=(1<<i); } } for(int s=1;s<(1<<n);s+=2){ if(!f[s]) continue; for(int u=0;u<n;++u) if(f[s]>>u&1) res[u]|=f[((1<<n)-1)^s^1]; } for(int u=0;u<n;++u){ for(int v=0;v<n;++v) if(res[u]>>v&1) putchar('1'); else putchar('0'); putchar('\n'); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现