复习2 - 图论
1.最小生成树
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,num,head[5010],ans,dis[5010]; bool vis[5010]; struct node{ int to,v,pre; }e[400010]; void Insert(int from,int to,int v){ e[++num].pre=head[from]; e[num].to=to; e[num].v=v; head[from]=num; } void Prim(){ memset(dis,127/3,sizeof(dis)); dis[1]=0; for(int i=1;i<=n;i++){ int minn=0x7fffffff,k=0; for(int j=1;j<=n;j++){ if(!vis[j]&&dis[j]<minn){ minn=dis[j]; k=j; } } if(minn==0x7fffffff)break; vis[k]=1; for(int j=head[k];j;j=e[j].pre){ int f=e[j].to; if(!vis[f]) dis[f]=min(dis[f],e[j].v); } } for(int i=1;i<=n;i++)ans+=dis[i]; } int main(){ scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); Insert(x,y,z);Insert(y,x,z); } Prim(); printf("%d",ans); }
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,num,head[5010],fa[5010]; struct node{ int x,y,z; }E[200010]; bool cmp(node a,node b){ return a.z<b.z; } int find(int x){ if(x==fa[x])return x; return fa[x]=find(fa[x]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; int x,y,z,cnt=0,ans=0; for(int i=1;i<=m;i++)scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].z); sort(E+1,E+m+1,cmp); for(int i=1;i<=m;i++){ int f1=find(E[i].x),f2=find(E[i].y); if(f1!=f2){ fa[f1]=f2; ans+=E[i].z; cnt++; } if(cnt==n-1)break; } if(cnt<n-1)puts("orz"); else printf("%d",ans); }
2.最短路
struct Node{ int id,dist; bool operator < (const Node b)const{ return dist>b.dist; } }; bool Dij(int limit){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); priority_queue<Node>q; q.push(make_node(s,0)); dis[s]=0; while(!q.empty()){ Node cur=q.top();q.pop();int now=cur.id; if(w[now]>limit)continue; if(vis[now])continue; vis[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(w[to]>limit)continue; if(dis[to]>dis[now]+e[i].v){ dis[to]=dis[now]+e[i].v; q.push(make_node(to,dis[to])); } } } return dis[t]<=g; }
bool spfa(int limit){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); deque<int>q; q.push_back(s); dis[s]=0;vis[s]=1; while(!q.empty()){ int now=q.front();q.pop_front();vis[now]=0; if(w[now]>limit)continue; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(w[to]>limit)continue; if(dis[to]>dis[now]+e[i].v){ dis[to]=dis[now]+e[i].v; if(!vis[to]){ vis[to]=1; if(!q.empty()&&dis[to]<dis[q.front()])q.push_front(to); else q.push_back(to); } } } } return dis[t]<=g; }
3.匈牙利算法
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 1000010 int n,link[maxn],num,head[maxn*2]; bool vis[maxn]; struct node{ int to,pre; }e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } bool dfs(int x){ for(int i=head[x];i;i=e[i].pre){ int to=e[i].to; if(!vis[to]){ vis[to]=1; if(!link[to]||dfs(link[to])){ link[to]=x;return 1; } } } return 0; } int main(){ scanf("%d",&n); int x,y; for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); if(x<=n)Insert(x,i); if(y<=n)Insert(y,i); } for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); if(!dfs(i)){ printf("%d",i-1); return 0; } } printf("%d",n); return 0; }
4.Tarjan算法(强联通分量,桥,割)
无向图割点
对该图进行一次 Tarjan 算法(这里注意在搜索树中把无向边当做有向边看。即LOW[u]=min(LOW[u],DFN[v])(v 是 u 的祖先)的条件变为(v 是 u 的祖先且 v 不是 u 的父亲))这样之后枚举搜索树上的所有边(u,v),若存在 LOW[v]>=DNF[u],则 u 是割点。
无向图割边
对该图进行一次 Tarjan 算法(这里注意在搜索树中把无向边当做有向边看。即LOW[u]=min(LOW[u],DFN[v])(v 是 u 的祖先)的条件变为(v 是 u 的祖先且 v 不是 u 的父亲))这样之后枚举搜索树上的所有边(u,v),若存在 LOW[v]>DNF[u],则(u,v)为割边。
#include<iostream> #include<cstdio> #define MAXn 100000 #define MAXm 2000000 using namespace std; int dfn[MAXn],low[MAXn],head[MAXm],st[MAXn],belong[MAXn]; bool in_st[MAXn]; int ans,n,m,num,s_num,cnt,group_num; struct node{ int to,pre; }e[MAXm]; void Insert(int from,int to){ e[++num].pre=head[from]; e[num].to=to; head[from]=num; } void group(int u){ cnt++;st[++s_num]=u;dfn[u]=low[u]=cnt;in_st[u]=1; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(!dfn[v]){ group(v); if(low[v]<low[u])low[u]=low[v]; } else if(dfn[v]<low[u]) if(in_st[v]) low[u]=dfn[v]; } if(dfn[u]==low[u]){ group_num++; while(st[s_num]!=u){ in_st[st[s_num]]=0; belong[st[s_num]]=group_num; s_num--; } in_st[u]=0;s_num--; belong[u]=group_num; } } int main(){ freopen("Tarjan_group.txt","r",stdin); scanf("%d%d",&n,&m);int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Insert(x,y); } for(int i=1;i<=n;i++){ if(!dfn[i])group(i); } for(int i=1;i<=n;i++){ printf("%d ",belong[i]); }printf("\n%d",group_num); }
#include<iostream> #include<cstdio> #include<algorithm> #define maxn 100010 using namespace std; int dfn[maxn],low[maxn],cnt,ans; bool cut[maxn]; int n,m,num,head[maxn]; struct node{ int from,to,pre; }e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].from=from; e[num].pre=head[from]; head[from]=num; } void Tarjan(int u,int father){ int rc=0; cnt++;dfn[u]=low[u]=cnt; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(!dfn[v]){ Tarjan(v,father); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=father)cut[u]=1; if(u==father)rc++; } low[u]=min(low[u],dfn[v]); } if(u==father&&rc>=2)cut[u]=1; } int main(){ freopen("Cola.txt","r",stdin); scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Insert(x,y);Insert(y,x); } for(int i=1;i<=n;i++) if(!dfn[i])Tarjan(i,i); for(int i=1;i<=n;i++)if(cut[i])ans++; printf("%d\n",ans); for(int i=1;i<=n;i++)if(cut[i])printf("%d ",i); }
#include<iostream> using namespace std; #include<cstdio> #include<cstring> #include<vector> #define N 201 vector<int>G[N]; int n,m,low[N],dfn[N]; bool is_cut[N]; int father[N]; int tim=0; void input() { scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=m;++i) { scanf("%d%d",&a,&b); G[a].push_back(b);/*邻接表储存无向边*/ G[b].push_back(a); } } void Tarjan(int i,int Father) { father[i]=Father;/*记录每一个点的父亲*/ dfn[i]=low[i]=tim++; for(int j=0;j<G[i].size();++j) { int k=G[i][j]; if(dfn[k]==-1) { Tarjan(k,i); low[i]=min(low[i],low[k]); } else if(Father!=k)/*假如k是i的父亲的话,那么这就是无向边中的重边,有重边那么一定不是桥*/ low[i]=min(low[i],low[k]); } } void count() { int rootson=0; Tarjan(1,0); for(int i=2;i<=n;++i) { int v=father[i]; if(v==1) rootson++;/*统计根节点子树的个数,根节点的子树个数>=2,就是割点*/ else{ if(low[i]>=dfn[v])/*割点的条件*/ is_cut[v]=true; } } if(rootson>1) is_cut[1]=true; for(int i=1;i<=n;++i) if(is_cut[i]) printf("%d\n",i); for(int i=1;i<=n;++i) { int v=father[i]; if(v>0&&low[i]>dfn[v])/*桥的条件*/ printf("%d,%d\n",v,i); } } int main() { input(); memset(dfn,-1,sizeof(dfn)); memset(father,0,sizeof(father)); memset(low,-1,sizeof(low)); memset(is_cut,false,sizeof(is_cut)); count(); return 0; }
5.lca
#include<iostream> #include<cstdio> #include<cstring> #define maxn 500010 using namespace std; int n,q,root,fa[maxn][22],dep[maxn],num,head[maxn]; struct node{int to,pre;}e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void dfs(int now,int father){ fa[now][0]=father; dep[now]=dep[father]+1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; dfs(to,now); } } int lca(int a,int b){ if(dep[a]!=dep[b]){ if(dep[a]<dep[b])swap(a,b); for(int i=18;i>=0;i--) if(dep[fa[a][i]]>=dep[b])a=fa[a][i]; } if(a==b)return a; for(int i=18;i>=0;i--) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; if(a==b)return a; return fa[a][0]; } /*int get(int a,int delta){ for(int i=0;i<=21;i++){ if(delta&(1<<i))a=fa[a][i]; }return a; } int lca(int a,int b){ if(dep[a]<dep[b])swap(a,b); a=get(a,dep[a]-dep[b]); if(a==b) return a; for(int i=21;i>=0;i--) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; return fa[a][0]; }*/ int main(){ scanf("%d%d%d",&n,&q,&root); int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); Insert(x,y);Insert(y,x); } dfs(root,root); for(int j=1;j<=20;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; while(q--){ scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } }
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 1000010 int n,s1,s2,head[maxn*2],num,son[maxn],sz[maxn],top[maxn],fa[maxn],dep[maxn]; struct node{ int to,pre; }e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void dfs1(int u,int father){ sz[u]=1; fa[u]=father; dep[u]=dep[father]+1; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(v==father)continue; dfs1(v,u); sz[u]+=sz[v]; if(!son[u]||sz[v]>sz[son[u]])son[u]=v; } } void dfs2(int u,int father){ top[u]=father; if(son[u])dfs2(son[u],father); else return; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(v==fa[u]||v==son[u])continue; dfs2(v,v); } } int lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); x=fa[top[x]]; } if(dep[x]<dep[y])return x; else return y; } int main(){ freopen("Cola.txt","r",stdin); scanf("%d",&n); int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); Insert(y,x); Insert(x,y); } scanf("%d%d",&s1,&s2); dfs1(1,0); dfs2(1,1); printf("%d",lca(s1,s2)); }
6.树上差分
7.欧拉回路
定义:给定无孤立结点图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在一条回路经过G每条边有且仅有一次,称这条回路为欧拉回路。具有欧拉回路的图成为欧拉图。
欧拉回路存在的充要条件: 每个点的度为偶数(无向图) 每个点的入度出度相等(有向图)
欧拉路存在的必要条件: 有且仅有两个点的度为奇数(无向图) 总的入度和等于总的出度和,有且仅有两个点的入度、出度差为1,其他点相等(有向图)
void eular(int x){ for(int i=1;i<=52;i++){ if(cxt[x][i]){ cxt[x][i]=cxt[i][x]=0; eular(i); } } s[++cnt]=x; }