竞赛图的哈密顿路径与哈密顿回路
更新日志
2025/07/17:开工。前置知识
竞赛图
可以视作给无向完全图的每条边设置了一个方向产生的有向图。
哈密顿路径
一条只经过图上的边使得每个点都被经过且仅被经过 \(1\) 次的路径。
哈密顿回路
一条哈密顿路径,倘若末尾可以连向开头,那么就形成了一条哈密顿回路。
竞赛图有关性质
每个竞赛图必然存在哈密顿路径(Redei 定理),强连通竞赛图必然存在哈密顿回路(Camion-Moon 定理)。竞赛图缩点之后形成链状 DAG,且仍然是竞赛图。
以上性质都比较显然,可以感性理解或者自行搜索。
找哈密顿路径
以任意顺序依次向路径中加入当前图内的点即可,具体加入流程为:
- 若当前点指向路径开头,那么就加入到路径开头。
- 若当前点被路径结尾指向,那么就加入到路径结尾。
- 否则顺次遍历路径找到第一个当前点可指向的点,然后把当前点插入到这个点前面。这个选中的位置前一个点必然指向当前点,且必然存在这样一个点,因为当前点必然指向路径结尾。
找哈密顿回路
首先这必须得是一个强连通竞赛图。
为了方便,我们先找出一条哈密顿路径,然后在路径上顺序遍历更新。
假如我们当前已经得到了左侧一个环和右侧一条链,其中链就是哈密顿路径的一部分,现在有这条链的下一个节点。
首先必然存在链尾连向当前点的边,因此可以直接把当前点加入链尾。
然后遍历整个环,如果能找到一个位置,使得环上这一点存在向链头的连边,且链尾可以连向下一点,那么就把整条链插入这个位置。
初始时找到第一个连向路径开头的位置即可得到一个环了。
为什么这样必然有解,考虑这个竞赛图强连通,且在上述策略下,如果一个点无法加入环,必然是因为环中全是连向这个点的(由于是按哈密顿路径来依次搞得因此环内至少有一个点连向当前点),那么当链尾存在向环的连边时,前一个位置必然连向链头。而整个图强连通,因此最后一个点必然指向环。
模板就直接详见代码了。
例题
上面说过,缩点之后整个图仍是 DAG,那么每个强连通分量内都可以跑哈密顿回路,然后在缩完点的图内 topo 即可,不难发现缩完点后,一个强连通分量指向另一个强连通分量,那么前者内每个节点都存在指向后者内每个节点的边,因此除了起始分量需要以起始点为回路起点,此后的所有分量均可任意开头遍历且任意位置结束,这样找方案会简单很多。
这道题重点就在于输出方案以练习找哈密顿回路。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template<typename Type> using vec=vector<Type>;
template<typename Type> using grheap=priority_queue<Type>;
template<typename Type> using lrheap=priority_queue<Type,vector<Type>,greater<Type>>;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define per(i,x,y) for(int i=(x);i>=(y);--i)
#define repl(i,x,y) for(int i=(x);i<(y);++i)
#define ReadFile(f) freopen(#f".in","r",stdin);freopen(#f".out","w",stdout);
struct mint {
int x,mod;
inline mint(int o=0,int p=998244353){x=o;mod=p;}
inline mint & operator=(ll o){return x=o%mod,(x+=mod)>=mod&&(x-=mod),*this;}
inline mint & operator+=(mint o){return (x+=o.x)>=mod&&(x-=mod),*this;}
inline mint & operator-=(mint o){return (x-=o.x)<0&&(x+=mod),*this;}
inline mint & operator*=(mint o){return x=(ll)x*o.x%mod,*this;}
inline mint & operator^=(ll b){mint res(1,mod);for(;b;b>>=1,*this*=*this)if(b&1)res*=*this;return x=res.x,*this;}
inline mint & operator^=(mint o){return *this^=o.x;}
inline mint & operator/=(mint o){return *this*=(o^=(mod-2));}
inline mint & operator++(){return *this+=1;}
inline mint & operator--(){return *this-=1;}
inline mint operator++(int o){mint res=*this;return *this+=1,res;}
inline mint operator--(int o){mint res=*this;return *this-=1,res;}
friend inline mint operator+(mint a,mint b){return a+=b;}
friend inline mint operator-(mint a,mint b){return a-=b;}
friend inline mint operator*(mint a,mint b){return a*=b;}
friend inline mint operator/(mint a,mint b){return a/=b;}
friend inline mint operator^(mint a,ll b){return a^=b;}
friend inline mint operator^(mint a,mint b){return a^=b;}
friend inline bool operator<(mint a,mint b){return a.x<b.x;}
friend inline bool operator>(mint a,mint b){return a.x>b.x;}
friend inline bool operator<=(mint a,mint b){return a.x<=b.x;}
friend inline bool operator>=(mint a,mint b){return a.x>=b.x;}
friend inline bool operator==(mint a,mint b){return a.x==b.x;}
friend inline istream & operator>>(istream &in,mint &o){ll x;return in>>x,o=x,in;}
friend inline ostream & operator<<(ostream &ot,mint o){return ot<<o.x,ot;}
};
typedef double flt;
template<typename Type> inline void read(Type &x){x=0;Type f=1,s=1;char ch=getchar();while(!isdigit(ch)&&~ch){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch))x=x*10+ch-48,ch=getchar();if(ch=='.'){ch=getchar();while(isdigit(ch))s/=10,x=x+(ch-48)*s,ch=getchar();}x*=f;}
template<typename Type> inline void write(Type x){if(x<0)putchar('-'),x=-x;if(x>9)write(x/10);putchar(x%10+'0');}
inline void read(string &s){s.clear();char ch=getchar();while(isspace(ch))ch=getchar();while(!isspace(ch)&&~ch)s+=ch,ch=getchar();}
inline void read(char &ch){ch=getchar();while(isspace(ch))ch=getchar();}
inline void write(const string &s){for(auto c:s)putchar(c);}
inline void write(const char &ch){putchar(ch);}
inline void write(const flt &x){printf("%.18LF",(long double)x);}
inline void write(const mint &m){write(m.x);}
inline void write(const char *x){while(*x)putchar(*(x++));}
template<typename Type,typename...T> inline void read(Type &x,T&...y){read(x),read(y...);}
template<typename Type> inline void put(const Type &x,char ch='\n'){write(x);if(ch)putchar(ch);}
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=2e3+5;
int n;
vec<int> g[N];
int dcnt;
int dfn[N],low[N];
stack<int> stk;bool ins[N];
int scnt;
int scc[N],siz[N];
vec<int> SCC[N];
void tarjan(int now){
dfn[now]=low[now]=++dcnt;
stk.push(now),ins[now]=1;
for(auto nxt:g[now]){
if(!dfn[nxt]){
tarjan(nxt);
chmin(low[now],low[nxt]);
}else if(ins[nxt])chmin(low[now],dfn[nxt]);
}
if(low[now]>=dfn[now]){
++scnt;
while(1){
int tp=stk.top();
scc[tp]=scnt,++siz[scnt];
stk.pop();ins[tp]=0;
SCC[scnt].pub(tp);
if(tp==now)break;
}
}
}
vec<int> g2[N];
int d[N];
int tcnt;
int nb[N],arr[N];
void topo(){
queue<int> q;
rep(i,1,scnt)if(!d[i])q.push(i);
while(!q.empty()){
int now=q.front();q.pop();
arr[nb[now]=++tcnt]=now;
for(auto nxt:g2[now])if(!--d[nxt])q.push(nxt);
}
}
int suf[N];
int hd[N],tl,pne[N];
bool G[N][N];
void gpath(int sid){
hd[sid]=0,tl=0;
for(auto i:SCC[sid]){
if(!hd[sid]){
hd[sid]=tl=i;
continue;
}
if(G[i][hd[sid]])pne[i]=hd[sid],hd[sid]=i;
else if(G[tl][i])pne[tl]=i,tl=i;
else{
for(int j=hd[sid];1;j=pne[j])if(G[i][pne[j]]){
pne[i]=pne[j];
pne[j]=i;
break;
}
}
}
int k=0;
for(int i=hd[sid];i;i=pne[i])if(G[i][hd[sid]]){
k=i;
break;
}
for(int i=pne[k];i;i=pne[i]){
if(G[i][hd[sid]]&&G[k][pne[k]])k=i;
else{
for(int j=hd[sid];j!=k;j=pne[j]){
if(G[i][pne[j]]&&G[j][pne[k]]){
int be=pne[k];
pne[k]=pne[i];
pne[i]=pne[j];
pne[j]=be;
i=k;
break;
}
}
}
}
if(!k)k=hd[sid];
pne[k]=hd[sid];
if(siz[sid]>1)for(int i=pne[k];1;i=pne[i]){
assert(G[i][pne[i]]);
if(i==k)break;
}
}
inline void Main(){
read(n);
rep(i,1,n)repl(j,1,i){
int o;read(o);
if(o)g[j].pub(i);
else g[i].pub(j);
}
rep(i,1,n)if(!dfn[i])tarjan(i);
rep(i,1,n)for(auto j:g[i]){
if(scc[i]!=scc[j])g2[scc[i]].pub(scc[j]),++d[scc[j]];
else G[i][j]=1;
}
topo();
per(i,scnt,1)suf[i]=suf[i+1]+siz[arr[i]];
rep(i,1,scnt)gpath(i);
rep(i,1,n){
put(suf[nb[scc[i]]],' ');
put(i,' ');
for(int j=pne[i];j!=i;j=pne[j])put(j,' ');
rep(j,nb[scc[i]]+1,scnt){
put(hd[arr[j]],' ');
for(int k=pne[hd[arr[j]]];k!=hd[arr[j]];k=pne[k])put(k,' ');
}
puts("");
}
}
int main(){
// ReadFile(___);
// #define MultiTasks
#ifdef MultiTasks
int t;read(t);while(t--)
#endif
Main();
return 0;
}

浙公网安备 33010602011771号