图论加深
一些较基础的知识点:
例一 P1196 银河英雄传说
带权并查集,比较简单。
设 表示点 到并查集根节点的距离。
那么我们只要在 find 和 merge 的时候各统计一下,再弄一个 ,就可以做了。
Code
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=3e4+5; int t; int fa[MAXN],dis[MAXN],siz[MAXN]; inline int find(int x) { if(x==fa[x]) return fa[x]; int f=find(fa[x]); dis[x]+=dis[fa[x]]; return fa[x]=f; } inline void merge(int x,int y) { int a=find(x),b=find(y); if(a==b) return; fa[a]=b; dis[a]+=siz[b]; siz[b]+=siz[a]; return; } int main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>t; for(register int i=1;i<=30000;i++) fa[i]=i,siz[i]=1; while(t--) { char op; cin>>op; if(op=='M') { int a,b; cin>>a>>b; merge(a,b); } else { int a,b; cin>>a>>b; int x=find(a),y=find(b); if(x!=y) printf("-1\n"); else printf("%d\n",abs(dis[a]-dis[b])-1); } } return 0; }
例二 CF687D Dividing Kingdom II
同样是一道并查集的题目,只不过带了一点贪心。
题意:
1.对于所有边,编号在 内的,将他们的点划分到两个集合 ;
2.对于每两个集合 取最大权值;
3.对于所有划分取最小值。
分析:
1.我们贪心的想,先将边的权值从大到小排序,那么每次拆的就会尽可能大;
2.然后我们将 数组开两倍,将 合并,将 合并,就可以保证他们不在同一集合;
3.若操作无法执行,那么就证明 已在同一集合内。
Code
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=1e3+5; const int MAXM=5e5+5; struct edge { int st,en,len,id; }e[MAXM]; int n,m,q; int fa[MAXN<<1]; inline int find(int x) { if(x==fa[x]) return fa[x]; return fa[x]=find(fa[x]); } inline void merge(int x,int y) { int a=find(x),b=find(y); if(a!=b) fa[a]=b; return; } inline bool cmp(edge x,edge y) { return x.len>y.len; } int main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m>>q; for(register int i=1;i<=m;i++) { cin>>e[i].st>>e[i].en>>e[i].len; e[i].id=i; } sort(e+1,e+m+1,cmp); while(q--) { int l,r,ans=-1; cin>>l>>r; for(register int i=1;i<=n*2;i++) fa[i]=i; for(register int i=1;i<=m;i++) if(e[i].id>=l && e[i].id<=r) { int a=find(e[i].st),b=find(e[i].en); if(a==b) { ans=e[i].len; break; } else { merge(e[i].st+n,e[i].en); merge(e[i].st,e[i].en+n); } } printf("%d\n",ans); } return 0; }
例三 P3623 免费道路
首先我们给出如下定义:
必选的鹅卵石路指将所有水泥路全部选完后图仍未连通,需要选的鹅卵石路,其他鹅卵石路为可选的鹅卵石路。
分析:
1.我们可以先将所有水泥路尝试连成生成树,边数为 ;
2.然后跑一遍 ,检查连通性并统计出必选的鹅卵石路的条数 ;
3.之后先连必选的鹅卵石路,然后连可选的连到 条,剩下的用水泥路连,最后跑一遍 即可。
那么我们就有以下几种 No Solution 的情况:
1.整个图根本就不连通;
2.必选的鹅卵石的数量大于 ,也就是说无法将所有必选的全选上;
3.所有鹅卵石路不足 。
所以说这道题思路不难,但要打满分很难。
Code
点击查看代码
#include<bits/stdc++.h> using namespace std; const int MAXN=2e5+5; struct edge { int x,y,z; }e[MAXN],ans[MAXN]; inline bool cmp1(edge a,edge b) { return a.z>b.z; } inline bool cmp2(edge a,edge b) { return a.z<b.z; } int n,m,k; int fa[MAXN]; int cnt,tot; inline void init() { cnt=tot=0; for(register int i=1;i<=n;i++) fa[i]=i; return; } inline int find(int x) { if(x==fa[x]) return fa[x]; return fa[x]=find(fa[x]); } inline void merge(int x,int y) { int a=find(x),b=find(y); if(a!=b) fa[a]=b; return; } inline bool check() { int f=find(1); for(register int i=2;i<=n;i++) if(find(i)!=f) return true; return false; } int main() { ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m>>k; for(register int i=1;i<=m;i++) cin>>e[i].x>>e[i].y>>e[i].z; init(); sort(e+1,e+m+1,cmp1); for(register int i=1;i<=m;i++) { int a=find(e[i].x),b=find(e[i].y),c=e[i].z; if(a!=b && !c) tot++,e[i].z=-1; merge(e[i].x,e[i].y); } if(tot>k || check()) { printf("no solution\n"); return 0; } init(); sort(e+1,e+m+1,cmp2); for(register int i=1;i<=m;i++) { int a=find(e[i].x),b=find(e[i].y),c=e[i].z; if(a!=b) if(c==1 || tot<k) { ans[++cnt]=e[i]; fa[a]=b; if(c<1) tot++,e[i].z=0; } } if(tot<k || check()) { printf("no solution\n"); return 0; } for(register int i=1;i<=cnt;i++) { if(ans[i].z==-1) ans[i].z=0; printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].z); } return 0; }
欧拉路径与欧拉回路
1.无向图的欧拉路径
在一张无向图 中,存在一条路径可以不重复地经过每一条边。
2.欧拉回路
欧拉回路是一条特殊的欧拉路径,起点和终点重合。
3.无向图存在欧拉路径的充分必要条件
度数为奇数的点的个数要么是 个,要么是 个。
4.实现方法
-
判定是否有解
-
选取一个度数为奇数的点作为起点
-
搜索每一条边并标记
-
存储经过的顶点必须在递归之后
5.有向图的欧拉路径与欧拉回路
与上面相同。
6.判定方法
-
入度-出度为 的点数恰好为 个
-
出度-入度为 的点数恰好为 个
或者
- 所有点入度=出度。
汉密尔顿环问题
在一张无向图 中,存在一条路径可以不重复地经过每一个点。
这个问题目前没有非暴力的解法,属于 难题。
例一 P7771 欧拉路径
欧拉路径板子题。
这道题卡时间卡得有点紧,需要加些优化。
点击查看代码
#include <bits/stdc++.h> using namespace std; const int MAXN=1e5+5; // char buf[1<<21],*p1=buf,*p2=buf; // #define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } vector<int>e[MAXN]; int n,m; int ru[MAXN],chu[MAXN]; int stk[MAXN],top; int head[MAXN]; inline void dfs(int x) { for(int i=head[x];i<e[x].size();i=head[x]) { head[x]=i+1; dfs(e[x][i]); } stk[++top]=x; } int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x].push_back(y); chu[x]++,ru[y]++; } for(int i=1;i<=n;i++) sort(e[i].begin(),e[i].end()); int id=1,cnt1=0,cnt2=0; bool flag=true; for(int i=1;i<=n;i++) { if(chu[i]!=ru[i]) flag=false; if(chu[i]-ru[i]==1) cnt1++,id=i; if(ru[i]-chu[i]==1) cnt2++; } if((!flag) && !(cnt1==cnt2 && cnt2==1)) { printf("No\n"); return 0; } dfs(id); while(top) printf("%d ",stk[top--]); return 0; }
例二 P2731 [USACO3.3]骑马修栅栏 Riding the Fences
欧拉回路板子题。
点击查看代码
#include <bits/stdc++.h> using namespace std; const int MAXN=505; // char buf[1<<21],*p1=buf,*p2=buf; // #define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } int m; int e[MAXN][MAXN]; int stk[MAXN],top; int du[MAXN<<1]; inline void dfs(int x) { for(int i=1;i<=500;i++) if(e[x][i]) e[x][i]--,e[i][x]--,dfs(i); stk[++top]=x; return; } int main() { m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); e[x][y]++,e[y][x]++; du[x]++,du[y]++; } vector<int>V; for(int i=1;i<=500;i++) if(du[i]&1) V.push_back(i); if(!V.size()) dfs(1); else dfs(min(V[0],V[1])); while(top) printf("%d\n",stk[top--]); return 0; }
本文作者:Code_AC
本文链接:https://www.cnblogs.com/code-ac/p/16683910.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步