不相信自己的人,连努力的价值都没有。|

Code_AC

园龄:3年粉丝:5关注:3

图论加深

一些较基础的知识点:

例一 P1196 银河英雄传说

带权并查集,比较简单。

disi 表示点 i 到并查集根节点的距离。

那么我们只要在 find 和 merge 的时候各统计一下,再弄一个 sizi,就可以做了。

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.对于所有边,编号在 [l,r] 内的,将他们的点划分到两个集合 S1,S2
2.对于每两个集合 S1,S2 取最大权值;
3.对于所有划分取最小值。

分析:
1.我们贪心的想,先将边的权值从大到小排序,那么每次拆的就会尽可能大;
2.然后我们将 fa 数组开两倍,将 a,b+n 合并,将 a+n,b 合并,就可以保证他们不在同一集合;
3.若操作无法执行,那么就证明 a,b 已在同一集合内。

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.我们可以先将所有水泥路尝试连成生成树,边数为 tot(totn1)
2.然后跑一遍 Kruscal,检查连通性并统计出必选的鹅卵石路的条数 cnt=n1tot
3.之后先连必选的鹅卵石路,然后连可选的连到 k 条,剩下的用水泥路连,最后跑一遍 Kruscal 即可。

那么我们就有以下几种 No Solution 的情况:
1.整个图根本就不连通;
2.必选的鹅卵石的数量大于 k,也就是说无法将所有必选的全选上;
3.所有鹅卵石路不足 k

所以说这道题思路不难,但要打满分很难。

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.无向图的欧拉路径
在一张无向图 G 中,存在一条路径可以不重复地经过每一条边。

2.欧拉回路
欧拉回路是一条特殊的欧拉路径,起点和终点重合。

3.无向图存在欧拉路径的充分必要条件
度数为奇数的点的个数要么是 0 个,要么是 2 个。

4.实现方法

  • 判定是否有解

  • 选取一个度数为奇数的点作为起点

  • dfs 搜索每一条边并标记

  • 存储经过的顶点必须在递归之后

5.有向图的欧拉路径与欧拉回路
与上面相同。

6.判定方法

  • 入度-出度为 1 的点数恰好为 1

  • 出度-入度为 1 的点数恰好为 1

或者

  • 所有点入度=出度。

汉密尔顿环问题

在一张无向图 G 中,存在一条路径可以不重复地经过每一个点。

这个问题目前没有非暴力的解法,属于 NP 难题

例一 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 中国大陆许可协议进行许可。

posted @   Code_AC  阅读(31)  评论(2编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起