简谈图论重要性&&图论总结
从外地学习回来,我对图论才有认识(以前就没接触过,非常尴尬),说实话,学好图论的重要性,就像学数学时在进行解析几何时,图极有可能是打开答案的最后秘钥,也就是数形结合,而懂的人永远明白,用图解决绝对比用解析简单(一般情况)。而图论对于oi选手说,就是一大杀器,有可能利己,也可能抱憾终身。所以说图论的重要性就很显然了。
大家在进入图论的时候,应该先掌握链式前向星建图,当然也可以叫邻接表,先附上我喜欢的模板
struct node{ int next,to,w; }edge[maxn<<4]; int head[maxn],cent; void add(int u,int v,int w){ edge[++cent]=(node){head[u],v,w}; head[u]=cent; }
——所谓模板,也就是自己喜欢的颜色涂上而已。
当然还有一些其他知识,比如说vector建图,这种建图的方式优点是难度小,而且还可以排序,这个在NOIP2018的D2T1上有极大优势。
scan(a),scan(b);
vec[a].push_back(b);
vec[b].push_back(a);
然后只要简单地定义排序一下,即可用食。
还有树形dp,这在树形图中将是一大助力,这里是dp求直径。
void dp(int s,int fa){ for(int i=head[s];i;i=edge[i].next){ int y=edge[i].to; if(vi[y]) continue; if(y==fa) continue; dp(y,s); an=max(anx,root[x]+root[y]+edge[i].w); root[s]=max(root[s],root[y]+edge[i].w); } }
在入门之后,请仔细思考与总结
总结方法:
1. 反向建边 例题
2. 路径记数,加法原理,并加上限制条件 例题
3. 巧妙运用二分图的检验 例题
4. 学习二分图的技巧,学会在只有两种条件有关系时,转化成二分图 例题
5. 深刻理解floyd的逐个点处理 例题
6. 二分答案对于路径长度和其他条件的单调性处理 如4中例题
7. 分清SPFA(没死透)和Dij两者各自的优势,注意负环
8. 在最小生成树中,注意prim和kruskal各自的优势 例题1 例题2
9. 差分约束 SPFA的独特优势(牢记系统约束) 例题
10. 在连通性中巧用度(即入度和出度) 例题
11. 善于建超级原点
(欢迎评价添加)
拓展性模板
在二分图中,匈牙利算法虽好,但是毕竟抵不过网络流做法,这里是dinic模板
#include<bits/stdc++.h> #define maxn 10008 using namespace std; int n,m,head[maxn],s,t,cent=1,d[maxn],maxflow; int min(int a,int b){return a<b?a:b;} const int inf=1<<30; struct node{ int next,to,w; }edge[maxn<<5]; queue<int >q; void add(int u,int v,int w){ edge[++cent]=(node){head[u],v,w};head[u]=cent; edge[++cent]=(node){head[v],u,0};head[v]=cent; } bool bfs(){ memset(d,0,sizeof d); while(q.size()) q.pop(); q.push(s),d[s]=1; while(!q.empty()){ int x=q.front();q.pop(); for(int i=head[x];i;i=edge[i].next){ int y=edge[i].to; if(edge[i].w&&!d[y]){ q.push(y);d[y]=d[x]+1; if(y==t) return 1; } } } return 0; } int Dinic(int x,int flow){ if(x==t) return flow; int rest=flow,k,y; for(int i=head[x];i;i=edge[i].next){ if(edge[i].w&&d[y=edge[i].to]==d[x]+1){ k=Dinic(y,min(rest,edge[i].w)); edge[i].w-=k; edge[i^1].w+=k; rest-=k; } } return flow-rest; } int main(){ scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=1,a,b,w;i<=m;i++){ scanf("%d%d%d",&a,&b,&w); add(a,b,w); } int flow=0; while(bfs()) while(flow=Dinic(s,inf)) maxflow+=flow; printf("%d",maxflow); }
缩点
在联通性中,缩点是必要的
#include<bits/stdc++.h> #define maxn 10007 using namespace std; int n,m,head[maxn],a[maxn],cent,stackk[maxn],cnt,tot[maxn],col[maxn]; int dfn[maxn],low[maxn],t,vis[maxn],top,root[maxn],ans; inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} struct node{ int next,to,from; }edge[maxn<<5]; void add(int u,int v){ edge[++cent]=(node){head[u],v,u};head[u]=cent; } void Tarjan(int x){ dfn[x]=low[x]=++t;vis[x]=1; stackk[++top]=x; for(int i=head[x];i;i=edge[i].next){ int y=edge[i].to; if(!dfn[y]){ Tarjan(y); low[x]=min(low[x],low[y]); }else if(vis[y]) low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]){ cnt++;int z; do{ z=stackk[top--]; col[z]=cnt; vis[z]=0; tot[cnt]+=a[z]; }while(z!=x); } } void dp(int x,int fa){ root[x]=tot[x];int ol=0; for(int i=head[x];i;i=edge[i].next){ int y=edge[i].to; if(y==fa) continue; dp(y,x); ans=max(ans,root[y]+root[x]); ol=max(ol,root[y]); } root[x]+=ol; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1,a,b;i<=m;i++){ scanf("%d%d",&a,&b); add(a,b); } for(int i=1;i<=n;i++){ if(!dfn[i]) Tarjan(i); } memset(head,0,sizeof(head)); for(int i=1;i<=m;i++){ int x=edge[i].from,y=edge[i].to; if(col[x]!=col[y]){ add(col[x],col[y]); } } for(int i=1;i<=cnt;i++){ if(root[i]) continue; dp(i,0); ans=max(root[i],ans); } printf("%d",ans); return 0; }
之后还有许多基础性的知识在以后会看到。