從外地學習回來,我對圖論才有認識(以前就沒接觸過,非常尷尬),說實話,學好圖論的重要性,就像學數學時在進行解析幾何時,圖極有可能是打開答案的最后秘鑰,也就是數形結合,而懂的人永遠明白,用圖解決絕對比用解析簡單(一般情況)。而圖論對於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; }
之后還有許多基礎性的知識在以后會看到。
