[关闭]
@910935974 2019-10-11T15:58:50.000000Z 字数 15714 阅读 524

算法笔记——图论

算法 图论


最短路

  1. for(int k=1;k<=n;++k)
  2. for(int i=1;i<=n;++i)
  3. for(int j=1;j<=n;++j)
  4. dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
  1. #include<cstdio>
  2. #include<cstring>
  3. #include<queue>
  4. #define maxn 200005
  5. #define maxm 800005
  6. using namespace std;
  7. priority_queue< pair<int,int> >qu;//堆优化dijkstra
  8. struct node
  9. {
  10. int v,val,next;
  11. }tr[maxm],ne[maxm];
  12. int n,m,s,e;
  13. int tot,head[maxn];
  14. int que[maxm];
  15. int dis[maxn],vis[maxn];
  16. void add(int x,int y,int z)
  17. {
  18. tot++;
  19. tr[tot].v=y;
  20. tr[tot].val=z;
  21. tr[tot].next=head[x];
  22. head[x]=tot;
  23. }
  24. void dj()
  25. {
  26. memset(dis,0x3f3f3f,sizeof(dis));
  27. dis[s]=0;
  28. qu.push(make_pair(0,s));
  29. while(qu.size())
  30. {
  31. int x=qu.top().second;//取出堆顶
  32. qu.pop();
  33. if(vis[x])continue;
  34. vis[x]=1;
  35. for(int t=head[x];t;t=tr[t].next)
  36. {
  37. int y=tr[t].v,z=tr[t].val;
  38. if(dis[y]>dis[x]+z)
  39. {
  40. dis[y]=dis[x]+z;
  41. qu.push(make_pair(-dis[y],y));
  42. }//把二元组插入堆
  43. }
  44. }
  45. }//堆优化的dijkstra
  46. int main()
  47. {
  48. freopen("data.in","r",stdin);
  49. freopen("data.out","w",stdout);
  50. scanf("%d%d%d",&n,&m,&s);
  51. for(int i=1;i<=m;++i)
  52. {
  53. int x,y,z;
  54. scanf("%d%d%d",&x,&y,&z);
  55. add(x,y,z);
  56. }
  57. dj();//dijkstra求最短路
  58. for(int i=1;i<=n;++i)
  59. printf("%d ",dis[i]);
  60. return 0;
  61. }
  1. #include<cstdio>
  2. #include<cstring>
  3. #define ll long long
  4. #define maxn 10005
  5. #define maxm 500005
  6. using namespace std;
  7. struct node
  8. {
  9. ll v,val,next;
  10. }tr[maxm];
  11. ll n,m,s;
  12. ll tot,head[maxn];
  13. ll dis[maxn],vis[maxn],q[maxm];
  14. void add(ll x,ll y,ll z)
  15. {
  16. tot++;
  17. tr[tot].v=y;
  18. tr[tot].next=head[x];
  19. tr[tot].val=z;
  20. head[x]=tot;
  21. }
  22. void spfa()
  23. {
  24. ll top=0,hd=0;
  25. memset(dis,0x3f3f3f3f,sizeof(dis));
  26. vis[s]=1;
  27. dis[s]=0;
  28. q[++top]=s;
  29. while(hd<top)
  30. {
  31. int x=q[++hd];
  32. vis[x]=0;
  33. for(ll t=head[x];t;t=tr[t].next)
  34. {
  35. ll y=tr[t].v,z=tr[t].val;
  36. if(dis[y]>dis[x]+z)
  37. {
  38. dis[y]=dis[x]+z;
  39. if(!vis[y])
  40. {
  41. q[++top]=y;
  42. vis[y]=1;
  43. }
  44. }
  45. }
  46. }
  47. }
  48. int main()
  49. {
  50. scanf("%lld%lld%lld",&n,&m,&s);
  51. for(ll i=1;i<=m;++i)
  52. {
  53. ll x,y,z;
  54. scanf("%lld%lld%lld",&x,&y,&z);
  55. add(x,y,z);
  56. }
  57. spfa();
  58. for(ll i=1;i<=n;++i)
  59. {
  60. if(dis[i]>=0x3f3f3f3f3f)
  61. printf("2147483647 ");
  62. else printf("%lld ",dis[i]);
  63. }
  64. return 0;
  65. }

分层图

1.dp法

  1. #include<cstdio>
  2. #include<queue>
  3. #include<cstring>
  4. #define maxn 10005
  5. #define maxm 50005
  6. #define maxk 15
  7. using namespace std;
  8. struct node
  9. {
  10. int v,val,next;
  11. }tr[maxm<<1];
  12. int n,m,k,s,t;
  13. int tot,head[maxn];
  14. int dis[maxn][maxk];
  15. priority_queue<pair<int,int> >que;
  16. void add(int x,int y,int z)
  17. {
  18. tot++;
  19. tr[tot].v=y;
  20. tr[tot].val=z;
  21. tr[tot].next=head[x];
  22. head[x]=tot;
  23. }
  24. void dijstra()
  25. {
  26. memset(dis,0x3f3f3f3f,sizeof(dis));
  27. for(int i=0;i<=k;++i)
  28. dis[s][i]=0;//初始化
  29. for(int i=0;i<=k;++i)//枚举次数
  30. {
  31. que.push(make_pair(0,s));
  32. while(que.size())
  33. {
  34. int x=que.top().second;
  35. que.pop();
  36. for(int t=head[x];t;t=tr[t].next)
  37. {
  38. int y=tr[t].v,z=tr[t].val,fl=0;
  39. if(i)
  40. {
  41. if(dis[y][i]>dis[x][i-1])//使用免费次数
  42. {
  43. dis[y][i]=dis[x][i-1];
  44. fl=1;
  45. }
  46. }
  47. if(dis[y][i]>dis[x][i]+z)//不使用次数
  48. {
  49. dis[y][i]=dis[x][i]+z;
  50. fl=1;
  51. }
  52. if(fl)
  53. que.push(make_pair(-dis[y][i],y));
  54. }
  55. }//分层图dp
  56. }
  57. }//堆优化dijstra
  58. int main()
  59. {
  60. scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
  61. s++;t++;
  62. for(int i=1;i<=m;++i)
  63. {
  64. int x,y,z;
  65. scanf("%d%d%d",&x,&y,&z);
  66. x++;y++;
  67. add(x,y,z);
  68. add(y,x,z);
  69. }
  70. dijstra();
  71. printf("%d",dis[t][k]);
  72. return 0;
  73. }

2.拆点法

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<queue>
  4. #define maxn 1000005
  5. #define maxm 20000005
  6. #define maxk 25
  7. using namespace std;
  8. struct node
  9. {
  10. int v,val,next;
  11. }tr[maxm<<1];
  12. int n,m,k,ans;
  13. int tot,head[maxn];
  14. int dis[maxn],vis[maxn];
  15. priority_queue<pair<int,int> >que;
  16. void add(int x,int y,int z)
  17. {
  18. tot++;
  19. tr[tot].v=y;
  20. tr[tot].val=z;
  21. tr[tot].next=head[x];
  22. head[x]=tot;
  23. }
  24. void dijstra()
  25. {
  26. memset(dis,0x3f3f3f3f,sizeof(dis));
  27. dis[1]=0;
  28. que.push(make_pair(0,1));
  29. while(que.size())
  30. {
  31. int x=que.top().second;
  32. que.pop();
  33. if(vis[x])continue;
  34. vis[x]=1;
  35. for(int t=head[x];t;t=tr[t].next)
  36. {
  37. int y=tr[t].v,z=tr[t].val;
  38. if(dis[y]>dis[x]+z)
  39. {
  40. dis[y]=dis[x]+z;
  41. que.push(make_pair(-dis[y],y));
  42. }
  43. }
  44. }
  45. }//dijstra
  46. int main()
  47. {
  48. scanf("%d%d%d",&n,&m,&k);
  49. for(int i=1;i<=m;++i)
  50. {
  51. int x,y,z;
  52. scanf("%d%d%d",&x,&y,&z);
  53. add(x,y,z);
  54. add(y,x,z);
  55. for(int j=1;j<=k;++j)
  56. {
  57. add(j*n+x,j*n+y,z);
  58. add(j*n+y,j*n+x,z);//分层建图
  59. add((j-1)*n+x,j*n+y,0);
  60. add((j-1)*n+y,j*n+x,0);//每层建的权值为0
  61. }
  62. }
  63. dijstra();
  64. ans=dis[n];
  65. for(int i=1;i<=k;++i)
  66. ans=min(ans,dis[i*n+n]);
  67. printf("%d",ans);
  68. return 0;
  69. }

个人还是喜欢dp法。
模板题:
1.[USACO09FEB]改造路Revamping Trails
2.[JLOI2011]飞行路线
3.[BJWC2012]冻结

还有一种分层图可以用二分答案的方法解决,这里不细讲,给出一道例题:
电话线


生成树

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. struct node
  5. {
  6. int u,v,d;
  7. }len[200005];
  8. int m,n,fa[100005];
  9. int find(int x)
  10. {
  11. return fa[x]==x ? x : fa[x]=find(fa[x]);
  12. }//路径压缩
  13. void unnion(int x,int y)
  14. {
  15. x=find(x),y=find(y);
  16. fa[x]=y;
  17. }
  18. int judge(int x,int y)
  19. {
  20. x=find(x),y=find(y);
  21. if(x==y)return 1;
  22. return 0;
  23. }//手写并查集
  24. bool cmp(node x,node y)
  25. {
  26. return x.d<y.d;
  27. }
  28. int main()
  29. {
  30. scanf("%d%d",&n,&m);
  31. for(int i=1;i<=n;i++)
  32. fa[i]=i;
  33. for(int i=1;i<=m;i++)
  34. {
  35. int x,y,z;
  36. scanf("%d%d%d",&x,&y,&z);
  37. len[i].u=x,len[i].v=y,len[i].d=z;
  38. }
  39. sort(len+1,len+1+m,cmp);
  40. int sum=0,ans=0;
  41. for(int i=1;i<=m;i++)
  42. {
  43. if(judge(len[i].u,len[i].v))continue;
  44. unnion(len[i].u,len[i].v);
  45. sum++;
  46. ans+=len[i].d;
  47. if(sum==n-1)
  48. {
  49. printf("%d",ans);
  50. return 0;
  51. }
  52. }
  53. if(sum==n)printf("%d",ans);
  54. else printf("orz");
  55. return 0;
  56. }

差分约束

  1. #include<cstdio>
  2. #include<queue>
  3. #include<cstring>
  4. #define maxn 20005
  5. #define maxm 20005
  6. using namespace std;
  7. struct node
  8. {
  9. int v,val,next;
  10. }tr[maxm];
  11. int m,n;
  12. int tot,head[maxn];
  13. int cnt[maxn],dis[maxn],vis[maxn];
  14. queue<int>q;
  15. void add(int x,int y,int z)
  16. {
  17. tot++;
  18. tr[tot].v=y;
  19. tr[tot].val=z;
  20. tr[tot].next=head[x];
  21. head[x]=tot;
  22. }//建边
  23. int spfa()
  24. {
  25. memset(dis,0x3f3f3f3f,sizeof(dis));
  26. for(int i=1;i<=n;++i)
  27. {
  28. q.push(i);
  29. vis[i]=1;
  30. }
  31. dis[1]=0;
  32. while(q.size())
  33. {
  34. int x=q.front();
  35. q.pop();
  36. vis[x]=0;
  37. for(int t=head[x];t;t=tr[t].next)
  38. {
  39. int y=tr[t].v,z=tr[t].val;
  40. cnt[y]=cnt[x]+1;
  41. if(cnt[y]>n)
  42. return 0;//判负环
  43. if(dis[y]>dis[x]+z)
  44. {
  45. dis[y]=dis[x]+z;
  46. if(!vis[y])
  47. {
  48. q.push(y);
  49. vis[y]=1;
  50. }
  51. }//更新最短路
  52. }
  53. }
  54. return 1;
  55. }
  56. void work1()
  57. {
  58. int a,b,c;
  59. scanf("%d%d%d",&a,&b,&c);
  60. if(a==b&&c!=0)
  61. {
  62. printf("No");
  63. return;
  64. }
  65. add(b,a,-c);
  66. }//a-b>=c转化为b-a<=-c
  67. void work2()
  68. {
  69. int a,b,c;
  70. scanf("%d%d%d",&a,&b,&c);
  71. if(a==b&&c!=0)
  72. {
  73. printf("No");
  74. return;
  75. }
  76. add(a,b,c);
  77. }//a-b<=c
  78. void work3()
  79. {
  80. int a,b;
  81. scanf("%d%d",&a,&b);
  82. add(a,b,0);
  83. add(b,a,0);
  84. }//a-b==0转化为a-b<=0&&b-a<=0
  85. int main()
  86. {
  87. scanf("%d%d",&n,&m);
  88. for(int i=1;i<=m;++i)
  89. {
  90. int flag;
  91. scanf("%d",&flag);
  92. if(flag==1)
  93. work1();
  94. else if(flag==2)
  95. work2();
  96. else if(flag==3)
  97. work3();
  98. }
  99. if(!spfa())
  100. printf("No");
  101. else printf("Yes");
  102. return 0;
  103. }

模板题:小K的农场;


拓扑排序

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int indgr[105],stackk[105],s[105][105],n,top=0;
  4. int main()
  5. {
  6. scanf("%d",&n);
  7. for(int i=1;i<=n;i++)
  8. {
  9. int x;
  10. scanf("%d",&x);
  11. while(x!=0)
  12. {
  13. indgr[x]++;
  14. s[i][x]=1;
  15. scanf("%d",&x);
  16. }
  17. }//输入,记录入度
  18. for(int i=1;i<=n;i++)
  19. if(indgr[i]==0)
  20. {
  21. top++;
  22. stackk[top]=i;//初始化,将入度为0的点入栈
  23. }
  24. while(top>0)
  25. {
  26. int v=stackk[top];
  27. printf("%d ",v);
  28. top--;//顶点出栈
  29. for(int j=1;j<=n;j++)
  30. if(s[v][j]==1)
  31. {
  32. indgr[j]--;//删除与当前顶点相连的边
  33. if(indgr[j]==0)stackk[++top]=j;//入栈
  34. }
  35. }
  36. return 0;
  37. }

Tarjan算法及其应用

下面给出tarjan缩点加上拓扑排序的实例。
代码:

  1. #include<cstdio>
  2. #define maxn 10005
  3. #define maxm 50005
  4. using namespace std;
  5. struct node
  6. {
  7. int v,next;
  8. }tr[maxm];
  9. int n,m;
  10. int head[maxn],tot;
  11. int out[maxn];
  12. int stk[maxn],co[maxn],low[maxn],dfn[maxn],sum[maxn],top,col,num;
  13. int flag,ans;
  14. int min(int a,int b)
  15. {
  16. return a<b?a:b;
  17. }
  18. void add(int x,int y)
  19. {
  20. tot++;
  21. tr[tot].v=y;
  22. tr[tot].next=head[x];
  23. head[x]=tot;
  24. }
  25. void tarjan(int u)
  26. {
  27. stk[++top]=u;
  28. low[u]=dfn[u]=++num;
  29. for(int t=head[u];t;t=tr[t].next)
  30. {
  31. int v=tr[t].v;
  32. if(!dfn[v])
  33. {
  34. tarjan(v);
  35. low[u]=min(low[u],low[v]);
  36. }//更新树边
  37. else if(!co[v])
  38. {
  39. low[u]=min(low[u],dfn[v]);
  40. }
  41. }
  42. if(low[u]==dfn[u])
  43. {
  44. co[u]=++col;
  45. sum[col]++;
  46. while(stk[top]!=u)
  47. {
  48. co[stk[top--]]=col;
  49. sum[col]++;//统计联通块中的元素个数
  50. }
  51. top--;
  52. }
  53. }
  54. int main()
  55. {
  56. scanf("%d%d",&n,&m);
  57. for(int i=1;i<=m;++i)
  58. {
  59. int x,y;
  60. scanf("%d%d",&x,&y);
  61. add(x,y);
  62. }
  63. for(int i=1;i<=n;++i)
  64. {
  65. if(!dfn[i])
  66. tarjan(i);
  67. }
  68. for(int i=1;i<=n;++i)
  69. {
  70. for(int t=head[i];t;t=tr[t].next)
  71. {
  72. int v=tr[t].v;
  73. if(co[i]!=co[v])
  74. out[co[i]]++;//统计出度
  75. }
  76. }//建新图
  77. for(int i=1;i<=col;++i)
  78. {
  79. if(out[i]==0)
  80. {
  81. if(flag==1)
  82. {
  83. printf("0");
  84. return 0;
  85. }
  86. else
  87. {
  88. flag=1;
  89. ans=sum[i];
  90. }
  91. }
  92. }
  93. printf("%d",ans);
  94. return 0;
  95. }

模板题:【模板】缩点

所谓割点,即图中的一个点,去掉这个点和这个点所连的边后,能将图分成两个部分。
找割点的代码:

  1. #include<cstdio>
  2. #define maxn 5005
  3. using namespace std;
  4. struct node
  5. {
  6. int v,next;
  7. }tr[maxn<<1];
  8. int n,m;
  9. int tot,head[maxn];
  10. int timer,ans,root,dfn[maxn],low[maxn],cut[maxn];//dfn[x]是x的dfs序,low[x]是x可以扫到的dfs序最小的节点
  11. int min(int x,int y)
  12. {
  13. return x<y?x:y;
  14. }
  15. void add(int x,int y)
  16. {
  17. tot++;
  18. tr[tot].v=y;
  19. tr[tot].next=head[x];
  20. head[x]=tot;
  21. }//建图
  22. void tarjan(int u)
  23. {
  24. dfn[u]=low[u]=++timer;//初始化更新时间戳
  25. int tot=0;
  26. for(int t=head[u];t;t=tr[t].next)
  27. {
  28. int y=tr[t].v;
  29. if(!dfn[y])
  30. {
  31. tarjan(y);
  32. low[u]=min(low[u],low[y]);//更新low[u]
  33. if(low[y]>=dfn[u])
  34. {
  35. tot++;
  36. if(u!=root||tot>=2)
  37. cut[u]=1;//找到割点
  38. }//x可以把图分成两个不连通的部分
  39. }//若y未被扫描过则去扫描y
  40. else low[u]=min(low[u],dfn[y]);//更新low[x]
  41. }
  42. }
  43. int main()
  44. {
  45. scanf("%d%d",&n,&m);
  46. for(int i=1;i<=m;++i)
  47. {
  48. int x,y;
  49. scanf("%d%d",&x,&y);
  50. add(x,y);
  51. add(y,x);
  52. }
  53. for(int i=1;i<=n;++i)
  54. {
  55. if(!dfn[i])
  56. {
  57. root=i;
  58. tarjan(i);
  59. }
  60. }//扫描割点
  61. for(int i=1;i<=n;++i)
  62. {
  63. if(cut[i])
  64. ans++;
  65. }
  66. printf("%d",ans);
  67. return 0;
  68. }

割边,就是桥,即若去掉桥,则该图不连通。
代码:

  1. #include<cstdio>
  2. #define maxn 50005
  3. using namespace std;
  4. struct node
  5. {
  6. int v,next;
  7. }tr[maxn<<1];
  8. int n,m;
  9. int tot,head[maxn];
  10. int timer,ans,bridge[maxn<<1],dfn[maxn],low[maxn];
  11. int min(int x,int y)
  12. {
  13. return x<y?x:y;
  14. }
  15. void add(int x,int y)
  16. {
  17. tot++;
  18. tr[tot].v=y;
  19. tr[tot].next=head[x];
  20. head[x]=tot;
  21. }//建图
  22. void tarjan(int u,int in_edge)
  23. {
  24. dfn[u]=low[u]=++timer;
  25. for(int t=head[u];t;t=tr[t].next)
  26. {
  27. int y=tr[t].v;
  28. if(!dfn[y])
  29. {
  30. tarjan(y,t);
  31. low[u]=min(low[u],low[y]);
  32. if(low[y]>dfn[u])
  33. bridge[t]=bridge[t^1]=1;//标记割边(需标记双向边)
  34. }
  35. else if(t!=(in_edge^1))
  36. low[u]=min(low[u],dfn[y]);//我也看不懂QAQ
  37. }
  38. }
  39. int main()
  40. {
  41. scanf("%d%d",&n,&m);
  42. tot=1;
  43. for(int i=1;i<=m;++i)
  44. {
  45. int x,y;
  46. scanf("%d%d",&x,&y);
  47. add(x,y);
  48. add(y,x);
  49. }
  50. for(int i=1;i<=n;++i)
  51. {
  52. if(!dfn[i])
  53. tarjan(i,0);
  54. }
  55. for(int i=2;i<tot;i+=2)
  56. {
  57. if(bridge[i])
  58. ans++;
  59. }
  60. printf("%d",ans);
  61. return 0;
  62. }

因为以上内容均为省选内容,所以我说的比较简略(其实是我看不懂)。


2-sat

安利博客:2-sat思想及入门
所以,,,上代码:

  1. #include<cstdio>
  2. #define maxn 2000005
  3. using namespace std;
  4. struct node
  5. {
  6. int v,next;
  7. }tr[maxn<<1];
  8. int n,m;
  9. int tot,head[maxn];
  10. int timer,col,top,stk[maxn],dfn[maxn],low[maxn],co[maxn],vis[maxn];//tarjan判是否成立,求拓扑序列
  11. int ans[maxn];
  12. int min(int x,int y)
  13. {
  14. return x<y?x:y;
  15. }
  16. void add(int x,int y)
  17. {
  18. tot++;
  19. tr[tot].v=y;
  20. tr[tot].next=head[x];
  21. head[x]=tot;
  22. }
  23. void tarjan(int x)
  24. {
  25. stk[++top]=x;
  26. dfn[x]=low[x]=++timer;
  27. for(int t=head[x];t;t=tr[t].next)
  28. {
  29. int y=tr[t].v;
  30. if(!dfn[y])
  31. {
  32. tarjan(y);
  33. low[x]=min(low[x],low[y]);
  34. }
  35. else if(!co[y])
  36. low[x]=min(low[x],dfn[y]);
  37. }
  38. if(dfn[x]==low[x])
  39. {
  40. co[x]=++col;
  41. while(stk[top]!=x)
  42. {
  43. co[stk[top]]=col;
  44. top--;
  45. }
  46. top--;
  47. }//成环
  48. }//tajan求强连通分量和反着的拓扑序
  49. int main()
  50. {
  51. scanf("%d%d",&n,&m);
  52. for(int i=1;i<=m;++i)
  53. {
  54. int x,xx,y,yy;
  55. scanf("%d%d%d%d",&x,&xx,&y,&yy);
  56. add(x+n*(xx&1),y+n*(yy^1));
  57. add(y+n*(yy&1),x+n*(xx^1));//位运算优化
  58. }//x为真,x+n为假
  59. for(int i=1;i<=2*n;++i)
  60. {
  61. if(!dfn[i])
  62. tarjan(i);
  63. }
  64. for(int i=1;i<=n;++i)
  65. {
  66. if(co[i]==co[i+n])
  67. {
  68. printf("IMPOSSIBLE");
  69. return 0;
  70. }
  71. }//i和i+n在同一个强连通分量里
  72. printf("POSSIBLE\n");
  73. for(int i=1;i<=n;++i)
  74. printf("%d ",co[i]<co[i+n]);
  75. return 0;
  76. }
  77. /*
  78. if (va && vb)
  79. { // a, b 都真,-a -> b, -b -> a
  80. g[a + n].push_back(b);
  81. g[b + n].push_back(a);
  82. }
  83. else if (!va && vb)
  84. { // a 假 b 真,a -> b, -b -> -a
  85. g[a].push_back(b);
  86. g[b + n].push_back(a + n);
  87. }
  88. else if (va && !vb)
  89. { // a 真 b 假,-a -> -b, b -> a
  90. g[a + n].push_back(b + n);
  91. g[b].push_back(a);
  92. }
  93. else if (!va && !vb)
  94. { // a, b 都假,a -> -b, b -> -a
  95. g[a].push_back(b + n);
  96. g[b].push_back(a + n);
  97. }
  98. */

习题:
1.【模板】2-SAT 问题
2.[JSOI2010]满汉全席


二分图

  1. #include<cstdio>
  2. #include<cstring>
  3. using namespace std;
  4. #define maxn 405
  5. #define maxm 60005
  6. struct node
  7. {
  8. int v,next;
  9. }tr[maxm];
  10. int match[maxn],head[maxn],vis[maxn];
  11. int n,p,cnt,tot,timer;
  12. void add(int x,int y)
  13. {
  14. tot++;
  15. tr[tot].v=y;
  16. tr[tot].next=head[x];
  17. head[x]=tot;
  18. }
  19. int dfs(int x)//找增广路
  20. {
  21. for(int t=head[x];t;t=tr[t].next)
  22. {
  23. int y=tr[t].v;
  24. if(vis[y]==timer) continue;
  25. vis[y]=timer;
  26. if(!match[y]||dfs(match[y]))
  27. {
  28. match[y]=x;
  29. return 1;
  30. }
  31. }
  32. return 0;
  33. }
  34. int main()
  35. {
  36. int T;
  37. scanf("%d",&T);
  38. while(T--)
  39. {
  40. tot=0;
  41. scanf("%d%d",&p,&n);
  42. for(int i=1;i<=p;i++)
  43. {
  44. scanf("%d",&cnt);
  45. while(cnt--)
  46. {
  47. int x;
  48. scanf("%d",&x);
  49. add(i,p+x);
  50. }//建立二分图
  51. }
  52. int ans=0;
  53. for(int i=1;i<=p;i++)
  54. {
  55. timer++;
  56. if(dfs(i))ans++;
  57. }
  58. if(ans==p)
  59. printf("YES\n");
  60. else printf("NO\n");
  61. for(int i=1;i<=p+n;++i)head[i]=0;
  62. for(int i=1;i<=p+n;++i)match[i]=0;
  63. }
  64. return 0;
  65. }

网络流

因为种类太多,所以直接给模板了。
- 最大流(最小割)

  1. #include<cstdio>
  2. #include<cstring>
  3. #define maxn 10005
  4. #define maxm 200005
  5. using namespace std;
  6. struct node
  7. {
  8. int v,val,next;
  9. }tr[maxm];
  10. int n,m,s,e;
  11. int tot=1,head[maxn];//从2开始存,保证异或
  12. int timer,dis[maxn],qu[maxm],vis[maxn];
  13. int min(int a,int b)
  14. {
  15. return a<b?a:b;
  16. }
  17. void add(int x,int y,int z)
  18. {
  19. tot++;
  20. tr[tot].v=y;
  21. tr[tot].val=z;
  22. tr[tot].next=head[x];
  23. head[x]=tot;
  24. }
  25. void Add(int x,int y,int z)
  26. {
  27. add(x,y,z);
  28. add(y,x,0);
  29. }
  30. int bfs()
  31. {
  32. int hd=0,top=0;
  33. qu[++top]=s;
  34. dis[s]=0;
  35. vis[s]=++timer;//起点入队,timer判断入队时间
  36. while(hd<top)
  37. {
  38. int x=qu[++hd];
  39. for(int t=head[x];t!=-1;t=tr[t].next)
  40. {
  41. int y=tr[t].v,z=tr[t].val;
  42. if(z==0)continue;
  43. if(vis[y]!=timer)
  44. {
  45. vis[y]=timer;
  46. dis[y]=dis[x]+1;
  47. qu[++top]=y;
  48. }//拓展节点,寻找增广路
  49. }
  50. }
  51. return vis[e]==timer;//找到增广路返回1,反之返回2
  52. }
  53. int dfs(int x,int lim)
  54. {
  55. if(x==e)return lim;//到达终点
  56. int ans=0;
  57. for(int t=head[x];t!=-1;t=tr[t].next)
  58. {
  59. int y=tr[t].v,z=tr[t].val;
  60. if(z!=0&&dis[y]==dis[x]+1)//有流量且相连
  61. {
  62. int nw=dfs(y,min(z,lim));
  63. if(nw!=0)
  64. {
  65. tr[t].val-=nw;
  66. tr[t^1].val+=nw;
  67. lim-=nw;
  68. ans+=nw;
  69. if(lim==0)
  70. return ans;
  71. }
  72. }
  73. }
  74. return ans;
  75. }
  76. int dinic()
  77. {
  78. int ans=0;
  79. while(bfs())ans+=dfs(s,0x3f3f3f3f);
  80. return ans;
  81. }
  82. int main()
  83. {
  84. memset(head,-1,sizeof(head));
  85. scanf("%d%d%d%d",&n,&m,&s,&e);
  86. for(int i=1;i<=m;i++)
  87. {
  88. int x,y,z;
  89. scanf("%d%d%d",&x,&y,&z);
  90. Add(x,y,z);
  91. }
  92. printf("%d",dinic());
  93. return 0;
  94. }

模板:
1.【模板】网络最大流
2.酒店之王

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #define inf 0x3f3f3f3f
  5. #define maxn 50005
  6. #define maxm 150005
  7. using namespace std;
  8. struct node
  9. {
  10. int u,v,next,val,cost;
  11. }tr[maxm];
  12. int tot=1,head[maxn];
  13. int hd,tp,que[maxm<<2],dis[maxn],in[maxn],pre[maxn];//建队
  14. int n,m,s,t;
  15. int ans,ans2;
  16. void add(int x,int y,int w,int va)
  17. {
  18. tot++;
  19. tr[tot].u=x;
  20. tr[tot].v=y;
  21. tr[tot].next=head[x];
  22. tr[tot].val=w;
  23. tr[tot].cost=va;
  24. head[x]=tot;
  25. }//建边
  26. void Add(int x,int y,int w,int va)
  27. {
  28. add(x,y,w,va);
  29. add(y,x,0,-va);//建立反向边,注意单位费用为-va
  30. }
  31. void spfa()
  32. {
  33. memset(dis,inf,sizeof(dis));
  34. memset(que,0,sizeof(que));
  35. memset(in,0,sizeof(in));
  36. hd=tp=0;
  37. dis[s]=0;
  38. que[++tp]=s;
  39. in[s]=1;//初始化队列
  40. while(hd<tp)
  41. {
  42. int x=que[++hd];
  43. in[x]=0;
  44. for(int i=head[x];i!=-1;i=tr[i].next)
  45. {
  46. int y=tr[i].v;
  47. if(tr[i].val&&dis[y]>dis[x]+tr[i].cost)
  48. {
  49. pre[y]=i;//记录y连接的边
  50. dis[y]=dis[x]+tr[i].cost;
  51. if(!in[y])
  52. {
  53. que[++tp]=y;
  54. in[y]=1;
  55. }//将y入队,标记节点
  56. }//更新费用的最短路
  57. }
  58. }
  59. }//spfa求增广路
  60. void price()
  61. {
  62. while(spfa(),dis[t]<inf)
  63. {
  64. int nw=inf;
  65. for(int i=pre[t];i;i=pre[tr[i].u])
  66. nw=min(nw,tr[i].val);//求出最小剩余流量
  67. ans+=dis[t]*nw;
  68. ans2+=nw;
  69. for(int i=pre[t];i;i=pre[tr[i].u])
  70. {
  71. tr[i].val-=nw;
  72. tr[i^1].val+=nw;
  73. }
  74. }
  75. }//求出最小费用
  76. int main()
  77. {
  78. memset(head,-1,sizeof(head));
  79. scanf("%d%d%d%d",&n,&m,&s,&t);
  80. for(int i=1;i<=m;++i)
  81. {
  82. int x,y,w,va;
  83. scanf("%d%d%d%d",&x,&y,&w,&va);
  84. Add(x,y,w,va);
  85. }
  86. price();//求费用流
  87. printf("%d %d",ans2,ans);
  88. return 0;
  89. }

模板:【模板】最小费用最大流


线段树优化建图

因为是写了博客的,所以不细讲,直接见博客吧!
线段树优化建图
- 代码

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define maxn 4000005
  4. #define lid ls[k]
  5. #define rid rs[k]
  6. using namespace std;
  7. struct node
  8. {
  9. int v,next;
  10. }tr[maxn*10];
  11. int n,m,rt,ndnum,ans;
  12. int id[maxn],dp[maxn],nw[maxn],in[maxn];
  13. int ls[maxn*4],rs[maxn*4],fl[maxn*4];//线段树
  14. int tot,head[maxn];
  15. int hd,top,que[maxn];
  16. void add(int x,int y)
  17. {
  18. tot++;
  19. tr[tot].v=y;
  20. tr[tot].next=head[x];
  21. head[x]=tot;
  22. in[y]++;//统计入度
  23. }
  24. void build(int &k,int l,int r)
  25. {
  26. if(l==r)
  27. {
  28. k=l;
  29. return;
  30. }
  31. k=++ndnum;//传新编号
  32. int mid=(l+r)>>1;
  33. build(lid,l,mid);
  34. build(rid,mid+1,r);//下传左右儿子
  35. add(lid,k);
  36. add(rid,k);//向父节点连边
  37. }
  38. void addtr(int k,int L,int R,int l,int r)
  39. {
  40. if(L>=l&&R<=r)
  41. {
  42. add(k,ndnum);//等级小连等级大
  43. return;
  44. }
  45. int mid=(L+R)>>1;
  46. if(l<=mid)
  47. addtr(lid,L,mid,l,r);
  48. if(r>mid)
  49. addtr(rid,mid+1,R,l,r);
  50. }//线段树优化建边
  51. void topusort()
  52. {
  53. for(int i=1;i<=ndnum;++i)
  54. {
  55. if(!in[i])
  56. {
  57. que[++top]=i;
  58. if(i<=n)dp[i]=1;//赋初值
  59. }
  60. }
  61. while(hd<top)
  62. {
  63. int x=que[++hd];
  64. ans=max(ans,dp[x]);
  65. for(int t=head[x];t;t=tr[t].next)
  66. {
  67. int y=tr[t].v;
  68. dp[y]=max(dp[y],dp[x]+(y<=n));//状态转移
  69. in[y]--;
  70. if(!in[y])
  71. que[++top]=y;
  72. }
  73. }
  74. }
  75. int main()
  76. {
  77. freopen("c.in","r",stdin);
  78. freopen("c.out","w",stdout);
  79. scanf("%d%d",&n,&m);
  80. ndnum=n;//从n开始建边
  81. build(rt,1,n);
  82. for(int i=1;i<=m;++i)
  83. {
  84. int x;
  85. scanf("%d",&x);
  86. ndnum++;//建虚点
  87. for(int j=1;j<=x;++j)
  88. {
  89. scanf("%d",&nw[j]);
  90. add(ndnum,nw[j]);
  91. }
  92. for(int j=1;j<x;++j)
  93. {
  94. if(nw[j]+1!=nw[j+1])
  95. addtr(n+1,1,n,nw[j]+1,nw[j+1]-1);//线段树优化建边(等级小连等级大)
  96. }
  97. }
  98. topusort();
  99. printf("%d",ans);
  100. return 0;
  101. }

对应例题:
车站分级

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注