c_pat_哈密顿回路 & 最大点集 & 是否是旅行商路径 & 欧拉路径 & 最深的根(邻接矩阵存图)
Hamiltonian Cycle
哈密顿回路问题是找到一个包含图中每个顶点的简单回路。你需要做的是判断给定路径是否为哈密顿回路。
充分条件
从任意起点出发,都具有以下性质的回路成为哈密顿回路:
- 起点=终点
- 每个待判序列给出的结点个数等于n+1,否则,必然经过某一个结点多次
- 1~n个结点都能被访问
坑:写则顶点编号从1N,暗地里却设为12N...
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n,m,Q,a[2*N],g[N][N];
bool chk(int sz) {
if (sz!=n+1 || a[0]!=a[sz-1]) return false;
bool vis[N]; memset(vis, false, sizeof vis);
for (int i=1; i<sz; i++) {
if (!g[a[i-1]][a[i]]) return false;
vis[a[i]]=1;
}
for (int i=1; i<=n; i++) if (!vis[i])
return false;
return true;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m;
for (int i=0; i<m; i++) {
int u,v; cin>>u>>v;
g[u][v]=g[v][u]=1;
}
cin>>Q;
while (Q--) {
int sz; cin>>sz;
for (int i=0; i<sz; i++) cin>>a[i];
cout << (chk(sz) ? "YES" : "NO")<<'\n';
}
return 0;
}
Maximal Clique
在一个无向图中,如果一个顶点子集满足子集内的任意两个不同顶点之间都是相连的,那么这个顶点子集就被称为一个团。
如果一个团不能通过加入某个新的顶点来扩展成一个更大的团,那么该团就被称为最大团。
现在,你需要判断给定顶点子集能否构成一个最大团。
思路
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n,m,a[N],g[N][N];
bool is_clique(int sz) {
for (int i=0; i<sz-1; i++)
for (int j=i+1; j<sz; j++) if (!g[a[i]][a[j]])
return false;
return true;
}
bool is_maximal(int sz) {
bool in_a[N]; memset(in_a, false, sizeof in_a);
for (int i=0; i<sz; i++) in_a[a[i]]=true;
for (int i=1; i<=n; i++) if (!in_a[i]) { //如果不在a中的点能和a中所有点相连,则不是最大点集;换句话说,只要有一个不在a中的顶点不和a中所有点相连,则认定这个点集是最大的
bool is_allconn=true;
for (int j=0; j<sz; j++) if (!g[i][a[j]]) {
is_allconn=false;
break;
}
if (is_allconn) return false;
}
return true;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m;
for (int i=0; i<m; i++) {
int u,v; cin>>u>>v;
g[u][v]=g[v][u]=true;
}
int q; cin>>q;
while (q--) {
int k; cin>>k;
for (int i=0; i<k; i++) cin>>a[i];
if (is_clique(k)) {
cout<<(is_maximal(k) ? "Yes":"Not Maximal")<<'\n';
} else {
cout<<"Not a Clique\n";
}
}
return 0;
}
Travelling Salesman Problem
请你从给定的路径列表中找到最接近旅行商问题的解的路径。
输出 Shortest Dist(X) = TotalDist,X 是最接近旅行商问题解决方案的回路编号,TotalDist 是其总距离。
思路
tsp问题就是求解哈密顿回路
#include<bits/stdc++.h>
using namespace std;
const int N=205, inf=0x3f3f3f3f;
int n,m,a[N],g[N][N];
int get_cost(vector<int>& v) {
int n=v.size(), cost=0;
for (int i=1; i<v.size(); i++) cost+=g[v[i-1]][v[i]];
return cost;
}
bool is_all_conn(vector<int>& v) {
for (int i=1; i<v.size(); i++) if (g[v[i-1]][v[i]]<0)
return false;
return true;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m; memset(g, -inf, sizeof g);
for (int i=0; i<m; i++) {
int u,v,w; cin>>u>>v>>w;
g[u][v]=g[v][u]=w;
}
int q, minCost=inf, minCostIdx=0; cin>>q;
for (int c=1; c<=q; c++) {
int k; cin>>k;
vector<int> v(k);
unordered_set<int> st;
for (int i=0; i<k; i++) cin>>v[i], st.insert(v[i]);
int conn=is_all_conn(v);
if (st.size()<n || v.front()!=v.back() || !conn) {
int cost=get_cost(v);
if (!conn) printf("Path %d: NA (Not a TS cycle)\n", c);
else printf("Path %d: %d (Not a TS cycle)\n", c, cost);
} else {
int cost=get_cost(v);
if (v.size()==n+1) {
printf("Path %d: %d (TS simple cycle)\n", c, cost);
} else printf("Path %d: %d (TS cycle)\n", c, cost);
if (cost<minCost) minCost=cost, minCostIdx=c;
}
}
printf("Shortest Dist(%d) = %d", minCostIdx, minCost);
return 0;
}
Eulerian Path
事实证明,如果一个连通图的所有顶点的度数都为偶数,那么这个连通图具有欧拉回路,且这个图被称为欧拉图。
如果一个连通图中有两个顶点的度数为奇数,其他顶点的度数为偶数,那么所有欧拉路径都从其中一个度数为奇数的顶点开始,并在另一个度数为奇数的顶点结束。具有欧拉路径但不具有欧拉回路的图被称为半欧拉图。
现在,给定一个无向图,请你判断它是欧拉图、半欧拉图还是非欧拉图。
模拟即可
#include<bits/stdc++.h>
using namespace std;
const int N=505;
vector<int> g[N];
int d[N], vis[N];
int dfs(int u) {
int c=1;
vis[u]=1;
for (int v : g[u]) if (!vis[v]) {
c+=dfs(v);
}
return c;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n,m; cin>>n>>m;
for (int i=0; i<m; i++) {
int u,v; cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
d[u]++, d[v]++;
}
int odd_degree=0;
for (int i=1; i<=n; i++) if ((d[i]&1)==1)
odd_degree++;
int c=dfs(1);
for (int i=1; i<=n; i++) {
cout<<d[i];
if (i!=n) cout<<' ';
}
cout<<'\n';
if (c==n) {
if (odd_degree==0) cout<<"Eulerian\n";
else if (odd_degree==2) cout<<"Semi-Eulerian\n";
else cout<<"Non-Eulerian\n";
} else {
cout<<"Non-Eulerian\n";
}
return 0;
}
Deepest Root
一个无环连通图可以被视作一个树。树的高度取决于所选取的根节点。现在,你要找到可以使得树的高度最大的根节点。
思路
并查集检查树的合法性+暴力算最大深度maxd,并找到深度为maxd的结点
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int fa[N];
vector<int> g[N];
int find(int u) {
return fa[u]==u ? u : fa[u]=find(fa[u]);
}
void merge(int u, int v) {
int fu=find(u), fv=find(v);
if (fu!=fv) fa[fu]=fv;
}
int get_dep(int u, int fa) {
int d=1;
for (int v : g[u]) if (v!=fa) {
d=max(d, get_dep(v,u)+1);
}
return d;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin>>n;
for (int i=1; i<=n; i++) fa[i]=i;
for (int i=1; i<n; i++) {
int u,v; cin>>u>>v;
g[u].push_back(v), g[v].push_back(u);
merge(u,v);
}
int cnt=0;
for (int i=1; i<=n; i++) if (fa[i]==i) {
cnt++;
}
if (cnt>1) {
printf("Error: %d components\n",cnt);
} else {
vector<int> v;
int maxd=0;
//for (int i=1; i<=n; i++) maxd=max(maxd, get_dep(i,-1));
//for (int i=1; i<=n; i++) if (get_dep(i,-1)==maxd) v.push_back(i);
for (int i=1; i<=n; i++) { //为什么这样会高效一些
int d=get_dep(i,-1);
if (d>maxd) {
maxd=d;
v.clear();
v.push_back(i);
} else if (d==maxd) {
v.push_back(i);
}
}
sort(v.begin(), v.end());
for (int droot : v) printf("%d\n",droot);
}
return 0;
}