Codeforces Gym 100851 K King's Inspection ( 哈密顿回路 && 模拟 )
题意 : 给出 N 个点(最多 1e6 )和 M 条边 (最多 N + 20 条 )要你输出一条从 1 开始回到 1 的哈密顿回路路径,不存在则输出 " There is no route, Karl! "
分析 :
题意很简单明了
众所周知,哈密顿回路是个 NP-Hard 问题
这么多个点的哈密顿回路肯定是不能暴力去寻找的
但是关注到 M ≤ N + 20 这个特殊的条件
那就说明图中肯定有很多单向链
那么这题就很明确了,就把所有的单链缩成一个点
然后再去 DFS 暴力找
口胡起来貌似很简单,写起来是真的 _(´ཀ`」 ∠)_
写了我挺久的,主要是Debug了挺久
#include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; struct EDGE{ int v, nxt; }Edge[maxn<<1]; int Chain[maxn][2];///记录链的出度和入度 int N, M, Head[maxn], cnt; int id, st;///分配ID的计数变量、寻找哈密顿回路的起点 int IN[maxn], OUT[maxn];///每个点的入度和出度数量,方便判断一个节点是否为单链的节点 int ID[maxn], vtx[maxn];///每个节点重新分配的ID、每个ID对应的节点or链 bool isChain[maxn], vis[maxn];///判断是否是链、DFS中的判重数组 int c[maxn];///并查集数组,判断图是否弱连通 vector<int> arr;///存储最终答案 int Findset(int x) { int root = x; while(c[root] != root) root = c[root]; int idx; while(c[x] != root){ idx = c[x]; c[x] = root; x = idx; } return root; } inline void Join(int a, int b) { int A = Findset(a); int B = Findset(b); if(A != B) c[A] = B; } inline void init() { for(int i=0; i<maxn; i++) c[i] = i; memset(ID, -1, sizeof(ID)); memset(vtx, -1, sizeof(vtx)); memset(Head, -1, sizeof(Head)); memset(isChain, false, sizeof(isChain)); cnt = id = 0; st = -1; } inline void AddEdge(int From, int To) { Edge[cnt].v = To; Edge[cnt].nxt = Head[From]; Head[From] = cnt++; } inline void ColorChain(int v, int Eiv, int Eid) { Chain[id][0] = v; bool loop = false;///链有可能构成环、如果是环,则出入度是一样的,标记一下 while(!(IN[Eiv] != 1 || OUT[Eiv] != 1)){ if(ID[Eiv] == -1) { ID[Eiv] = id; if(vtx[id] == -1) vtx[id] = Eiv; } else { loop = true; break; } isChain[Eiv] = true; Eiv = Edge[Eid].v; Eid = Head[Eiv]; } Chain[id][1] = loop ? v : Eiv; id++; } inline void Color(int v) { if(IN[v] == 1 && OUT[v] == 1) return;///如果是单链上的节点就跳过,因为我的染色找链 ///总是去找链的入度节点才去为这个链分配ID Chain[id][0] = v, Chain[id][1] = -1;///可以无视这个,没啥用 ID[v] = id++; vtx[id-1] = v; int Eid, Eiv; for(int i=Head[v]; i!=-1; i=Edge[i].nxt){ Eiv = Edge[i].v; Eid = i; if(IN[Eiv] == 1 && OUT[Eiv] == 1)///说明v是一个链的入度 ColorChain(v, Eiv, Head[Eiv]);///为链染色 } } int ans[maxn]; bool DFS(int v, int num)///寻找哈密顿回路,分是否是链的两种情况 { if(num == id && v == st) return true; if(vis[ID[v]]) return false; vis[ID[v]] = true; ans[num] = ID[v]; if(isChain[v]){ if( DFS(Chain[ID[v]][1], num+1) )///如果是链的开头,直接递归进其出度 return true; }else{ for(int i=Head[v]; i!=-1; i=Edge[i].nxt)///如果不是链则正常访问其所有出度 if( DFS(Edge[i].v, num+1) ) return true;; } vis[ID[v]] = false; return false; } inline void Print_Chain(int v, int Chain_id) { while(IN[v]==1 && OUT[v]==1){ arr.push_back(v); v = Edge[Head[v]].v; } } int main(void) { freopen("king.in", "r", stdin);///提交记得加这两个 freopen freopen("king.out", "w", stdout); scanf("%d %d", &N, &M); init(); int From, To; while(M--){ scanf("%d %d", &From, &To); AddEdge(From, To); Join(From, To); IN[To]++, OUT[From]++; } int root = Findset(1); for(int i=2; i<=N; i++){ if(Findset(i) != root){ puts("There is no route, Karl!"); return 0; } } for(int i=1; i<=N; i++){ if(st == -1 && (IN[i] != 1 || OUT[i] != 1)) st = i; if(ID[i] != -1) continue; else Color(i);///给每一个点配上一个ID,如果是链,则链上顶点ID一致, ///且当前节点 isChain = true 标记是链 } if(id == 0){ int v = 1; bool flag = true; while(flag || v != 1){ flag = false; printf("%d ", v); v = Edge[Head[v]].v; }puts("1"); return 0; } memset(vis, false, sizeof(vis)); if(DFS(st, 0)){ for(int i=0; i<id; i++){ int cur = vtx[ans[i]]; if(isChain[cur]) Print_Chain(cur, ans[i]); else arr.push_back(cur); } bool BEGIN = false; int Count = 0; int len = arr.size(); for(int i=0; i<len && Count<len; i=(i+1)%len){///因为不保证总是从1开始dfs,所以要去调整一下输出 if(arr[i] == 1) BEGIN = true; if(BEGIN){ printf("%d ", arr[i]); Count++; } }puts("1"); }else puts("There is no route, Karl!"); return 0; } /* 10 11 10 8 5 4 9 1 4 3 7 2 2 6 6 5 8 9 1 7 10 3 3 10 */