4.29--4.30 图论
1、二叉堆的操作
(1)取出元素
(2)插入元素
(3)删除元素
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//手写大根堆维护 #include<bits/stdc++.h> using namespace std; int heap[1000]; int up(int x) { if(heap[x]<=heap[x/2]) return 0; else { swap(heap[x],heap[x/2]); up(x/2); } } int down(int x) { if(heap[x]>=heap[x*2]&&heap[x]>=heap[x*2+1]) return 0; else if(heap[x*2]>heap[x*2+1]) { swap(heap[x],heap[x*2]); down(x*2); } else { swap(heap[x],heap[x*2+1]); down(x*2+1); } } int m,od,x,n; int main() { scanf("%d",&m); while(m--) { scanf("%d",&od); if(od==1) { printf("%d",heap[0]); } if(od==2) { scanf("%d",&x); n++; heap[n]=x; up(n); } if(od==3) { heap[0]=heap[n]; n--; down(1); } } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; priority_queue<int>heap; int m,x,od; int main() { scanf("%d",&m); while(m--) { scanf("%d",&od); if(od==1) printf("%d",heap.top()); if(od==2) { scanf("%d",&x); heap.push(x); } if(od==3) { heap.pop(); } } return 0; }
2、最小生成树
(1)prim算法 蓝白点思想
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=10000+15; int m,n,x,y,z,maxx,ans; int edge[maxn][maxn],dis[maxn]; bool boo[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z);//加边 edge[x][y]=edge[y][x]=z; } memset(dis,127/3,sizeof(dis)); dis[1]=0;boo[1]=1;//由结点1开始生成最小生成树,boo[1]=1,表示结点1已经加入最小生成树 for(int i=2;i<=n;i++){ if(edge[1][i]) dis[i]=min(dis[i],edge[1][i]);//更新和结点1相连的点的值 } for(int i=1;i<=n-1;i++){//生成n-1条边 maxx=0; for(int j=1;j<=n;j++){ if(!boo[j])//找一个点没加入最小生成树并且距离已经生成的树的距离最小的 if(!maxx||dis[maxx]>dis[j]) maxx=j; } boo[maxx]=1;//将它加入最小生成树 ans+=dis[maxx];//加入答案 for(int j=1;j<=n;j++){//将它更新其他点到最小生成树的距离; if(!boo[j]) if(edge[maxx][j]) dis[j]=min(dis[j],edge[maxx][j]); } } printf("%d",ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//prim的队列优化 #include <bits/stdc++.h> using namespace std; const int maxn=100000+15; const int maxm=100000+15; struct Edge { int x,y,z,next; Edge(int x=0,int y=0,int z=0,int next=0):x(x),y(y),z(z),next(next) {} }edge[maxm*2]; const bool operator < (const Edge &a,const Edge &b) //为了优先队列取边时,将权值最小的边先取出 { return a.z>b.z; } int n,m; int sumedge,head[maxn]; int ins(int x,int y,int z) { edge[++sumedge]=Edge(x,y,z,head[x]); return head[x]=sumedge; } priority_queue <Edge> que;//优先队列 里面的内容是一个结构体 是没条边的x,y,z,next; int ans; bool boo[maxn];//标记有没有加入最小生成树 int main() { scanf("%d%d",&n,&m);//输入n个点m条边 for (int i=1;i<=m;i++) { int x,y,z;//输入起点,终点,权值 scanf("%d%d%d",&x,&y,&z); ins(x,y,z);//链表建边 ins(y,x,z); } memset(boo,false,sizeof(boo));//没有加入最小生成树 boo[1]=true;//结点1加入最小生成树;从结点1开始生成最小生成树; for (int u=head[1];u;u=edge[u].next) que.push(edge[u]);//将和1相连的边入队 for (int i=1;i<n;i++) //总共有n-1条边 { Edge temp; temp=que.top();//取出距离1结点长度最小的边 for (;boo[temp.y];que.pop(),temp=que.top());//当这个边的终点已经加入最小生成树,就删除这条边,取次小的边 que.pop();//删除这条边 已经加入最小生成树 留着也没用 ans+=temp.z;//加上这条边的权值 boo[temp.y]=true;//这条边的终点加入最小生成树 temp是一个结构体 for (int u=head[temp.y];u;u=edge[u].next)//扫一遍和现在的点相连的边 if (!boo[edge[u].y]) que.push(edge[u]);//将没有在最小生成树中的边入队; } printf("%d\n",ans); return 0; }
(2)kruskal 贪心 并查集
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=100000; int n,m,x,y,z,far[maxn],ans; const int oo=1234567 struct Edge { int x,y,z; Edge(int x=0,int y=0,int z=oo): x(x),y(y),z(z) {} }edge[maxn]; bool cmp(Edge a,Edge b) { return a.z<b.z; } int f(int x) { return far[x]==x?x:far[x]=f(far[x]); } void unionn(int x,int y) { far[f(x)]=f(y); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); edge[i]=Edge(x,y,z);//这样对结构体赋值要在结构体里写个Edge(int ------) } sort(edge+1,edge+m+1,cmp); for(int i=1;i<=n;i++) { far[i]=i; } for(int i=1;i<=m;i++) { int p=edge[i].x,q=edge[i].y; if(f(p)!=f(q)){ unionn(p,q); ans+=edge[i].z; } } printf("%d",ans); return 0; }
3、最小瓶颈生成树
最小瓶颈生成树为使所有的生成树中最大权值最小
最小生成树一定是最小瓶颈生成树
最小瓶颈生成树不一定是最小生成树
4、最优比率生成树
二分思想
改日再添
5、最短路径
(1)dijkstra
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn=100000+15; const int maxm=100000+15; struct Edge { int x,y,z,next; Edge(int x=0,int y=0,int z=0,int next=0):x(x),y(y),z(z),next(next) {} }edge[maxm*2]; const bool operator < (const Edge &a,const Edge &b) { return a.z>b.z; } int n,m; int sumedge,head[maxn]; int ins(int x,int y,int z) { edge[++sumedge]=Edge(x,y,z,head[x]); return head[x]=sumedge; } priority_queue <Edge> que; int ans; bool boo[maxn]; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); ins(x,y,z); ins(y,x,z); } memset(boo,false,sizeof(boo)); boo[1]=true; for (int u=head[1];u;u=edge[u].next) que.push(edge[u]); for (int i=1;i<n;i++) //总共有n-1条边 { Edge temp; temp=que.top(); for (;boo[temp.y];que.pop(),temp=que.top()); que.pop(); ans+=temp.z; boo[temp.y]=true; for (int u=head[temp.y];u;u=edge[u].next) if (!boo[edge[u].y]) que.push(edge[u]); } printf("%d\n",ans); return 0; }
(2)bellman-ford
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn=1000; const int maxm=10000; const int oo=100000000; int n,m,x[maxm],y[maxm],z[maxm]; int dis[maxn]; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d%d",&x[i],&y[i],&z[i]); } for (int i=2;i<=n;i++) dis[i]=oo; for (int i=1;i<n;i++) for (int j=1;j<=m;j++) if (dis[x[j]]!=oo && dis[y[j]]>dis[x[j]]+z[j]) dis[y[j]]=dis[x[j]]+z[j]; for (int j=1;j<=m;j++) if (dis[x[j]]!=oo && dis[y[j]]>dis[x[j]]+z[j]) { printf("-1\n"); return 0; } for (int i=1;i<=n;i++) printf("%d ",dis[i]); printf("\n"); return 0; }
(3)floyed
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; int n,m,x,y,z; const int oo=123456,maxn=1234; int dis[maxn][maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) { dis[i][j]=oo*(i!=j);//当i和j相等时dis[i][j]赋值为0; } } for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); dis[x][y]=min(dis[x][y],z); } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); return 0; }
6、有向无环图的拓扑排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=100000; struct Edge { int x,y,next; Edge(int x=0,int y=0,int next=0):x(x),y(y),next(next){} }edge[maxn]; int head[maxn],sumedge,inn[maxn],que[maxn],Head=1,tail=0; void add_edge(int x,int y) { edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } int n,m,x,y; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add_edge(x,y); inn[y]++; } for(int i=1;i<=n;i++) { if(inn[i]==0) que[++tail]=i; } for(;Head<=tail;Head++) { int x=que[Head]; for(int u=head[x];u;u=edge[u].next) { inn[edge[u].y]--; if(inn[edge[u].y]==0) que[++tail]=edge[u].y; } } for(int i=1;i<=tail;i++) printf("%d ",que[i]); return 0; }
7、tarjian求强连通分量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//tarjian #include <bits/stdc++.h> using namespace std; const int maxn=100000+15; struct Edge { int x,y,next; Edge(int x=0,int y=0,int next=0): x(x),y(y),next(next) {} }edge[maxn]; int sumedge,head[maxn]; int n,m; int ins(int x,int y) { edge[++sumedge]=Edge(x,y,head[x]); return head[x]=sumedge; } bool instack[maxn]; int top,Stack[maxn]; int dfn[maxn],low[maxn],tim; bool vis[maxn]; int col[maxn],sumcol; int dfs(int now) { dfn[now]=low[now]=++tim; Stack[++top]=now; vis[now]=true;//入栈并且访问过 instack[now]=true; for (int u=head[now];u;u=edge[u].next) if (instack[edge[u].y])//如果在栈中 说明往回找边了 low[now]=min(low[now],dfn[edge[u].y]);//low和dfn求最小值 因为已经在栈中 就不是 now结点子树的结点 就要 low和dfn求最小值 else if (!vis[edge[u].y]) { dfs(edge[u].y); low[now]=min(low[now],low[edge[u].y]); } else//应经vis过并且不在栈中的 已经给它找到了连通图 不做处理 { } if (low[now]==dfn[now]) { sumcol++; col[now]=sumcol; while (Stack[top]!=now) { col[Stack[top]]=sumcol;//将一个连通图上染上一样的颜色 instack[Stack[top]]=false; top--; } instack[now]=false; top--; } return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y); } for (int i=1;i<=n;i++) if (!vis[i]) dfs(i); return 0; }
8、tarjian缩点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//缩点 #include <bits/stdc++.h> using namespace std; const int maxn=100000+15; struct Edge { int x,y,next; Edge(int x=0,int y=0,int next=0): x(x),y(y),next(next) {} }edge[maxn],edge2[maxn]; map <int, bool > Map[maxn]; int sumedge,head[maxn]; int n,m; int sumedge2,head2[maxn]; int ins2(int x,int y) { edge2[++sumedge2]=Edge(x,y,head2[x]); return head2[x]=sumedge; } int ins(int x,int y) { edge[++sumedge]=Edge(x,y,head[x]); return head[x]=sumedge; } bool instack[maxn]; int top,Stack[maxn]; int dfn[maxn],low[maxn],tim; bool vis[maxn]; int col[maxn],sumcol; int dfs(int now) { dfn[now]=low[now]=++tim; Stack[++top]=now; vis[now]=true; instack[now]=true; for (int u=head[now];u;u=edge[u].next) if (instack[edge[u].y]) low[now]=min(low[now],dfn[edge[u].y]); else if (!vis[edge[u].y]) { dfs(edge[u].y); low[now]=min(low[now],low[edge[u].y]); } else { } if (low[now]==dfn[now]) { sumcol++; col[now]=sumcol; while (Stack[top]!=now) { col[Stack[top]]=sumcol; instack[Stack[top]]=false; top--; } instack[now]=false; top--; } return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y); } for (int i=1;i<=n;i++) if (!vis[i]) dfs(i); for (int i=1;i<=n;i++) for (int u=head[i];u;u=edge[u].next) if (col[i]!=col[edge[u].y]) if (Map[col[i]].find(col[edge[u].y])==Map[col[i]].end()) //相当于 if(map[col[i]][col[edge[u].y]==false)如果这两个不在同一个强连通分量的点没有连起来 为了防止重复加边 所以加一个判断条件 //如果map[][] 点很多时开的太大 所以用Map[].find ,map<int,bool>Map[maxn]可以理解为bool型的Map[][]; { Map[col[i]][col[edge[u].y]]=true; ins2(col[i],col[edge[u].y]); } return 0;
9、tarjian求割点 割边
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn=100000+15; struct Edge { int x,y,next; Edge(int x=0,int y=0,int next=0): x(x),y(y),next(next) {} }edge[maxn*2]; int sumedge,head[maxn],n,m; int ins(int x,int y) { edge[++sumedge]=Edge(x,y,head[x]); return head[x]=sumedge; } int low[maxn],dfn[maxn],tim; bool vis[maxn]; bool cutedge[maxn],cutpoint[maxn]; int dfs(int now,int pre) //需要多记录父边的编号 { dfn[now]=low[now]=++tim; vis[now]=true; int sum=0; //树边数目 bool boo=false; for (int u=head[now];u;u=edge[u].next) if ((u^1)!=pre) //确定不是父边 if (!vis[edge[u].y]) { sum++; dfs(edge[u].y,u); if (low[edge[u].y]>dfn[now]) //判断割边 { cutedge[u/2]=true; //u/2为边的实际编号 } if (low[edge[u].y]>=dfn[now]) //判断割点 boo=true; low[now]=min(low[now],low[edge[u].y]); } else { low[now]=min(low[now],dfn[edge[u].y]); } if (pre==-1) //分情况判断割点 { if (sum>1) cutpoint[now]=true; } else { if (boo) cutpoint[now]=true; } return 0; } int main() { scanf("%d%d",&n,&m); sumedge=-1; //使得代表同一条边的两边编号更有关系 memset(head,-1,sizeof(head)); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x); } for (int i=1;i<=n;i++) if (!vis[i]) dfs(i,-1); return 0; }
10、二分图
(1)染色
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//O (M+N) 点数+边数 #include<bits/stdc++.h> using namespace std; const int maxn=10000+15; struct Edge { int x,y,next; Edge(int x=0,int y=0,int next=0):x(x),y(y),next(next){} }edge[maxn]; int head[maxn],col[maxn],que[maxn],sumedge,x,y,n,m,h,t; int add_edge(int x,int y) { edge[++sumedge]=Edge(x,y,head[x]); return head[x]=sumedge; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } memset(col,-1,sizeof(col)); que[h=t=1]=1; col[1]=0; for(;h<=t;h++) { int x=que[h]; for(int u=head[x];u;u=edge[u].next) if(col[edge[u].y]!=-1)//已经染上色 { if(col[edge[u].y]==col[x])//边的两边染了相同的颜色 { printf("Wrong!\n"); return 0; } } else { col[edge[u].y]=col[x]^1; que[++t]=edge[u].y; } } return 0; }
(2)并查集
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=1000+15; int far[maxn],n,m,x,y; int f(int x) { return far[x]==x?x:far[x]=f(far[x]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); int fx1=f(x*2),fy1=f(y*2-1);//一个染黑一个染白 far[fx1]=fy1; int fx2=f(x*2-1),fy2=f(y*2);//一个染白一个染黑 far[fx2]=fy2; } for(int i=1;i<=n;i++) { if(f(i*2)==f(i*2-1)) //如果这个点既染黑又染白 { printf("Wrong!"); return 0; } } }
//呼~终于整理完了
//这个学长好温柔啊QWQ