北京大学ACM暑期课讲义-网络流
现在假设每条边的容量都是整数
这个算法每次都能将流至少增加1 由于整个网络的流量最多不超过 图中所有的边的容 量和C,从而算法会结束 现在来看复杂度 找增广路径的算法可以用dfs, 复杂度为边数m+顶 点数n Dfs 最多运行C次 所以时间复杂度为C*(m+n) =C* n^2
这个算法实现很简单 但是注意到在图中C可能很大很大 比如说下面这张图
a
100 s 100 100
100 100 b
t
如果我们沿着s-a-b-t路线走 仅能得到一个100的流
a 100 s 100 100
100
100
t
实际上此图存在流量 为200的流
b
问题出在过早地认为边a → b上流量不为0,因而“ 封锁”了流量继续增大的可能。 一个改进的思路:应能够修改已建立的流网络,使得 “不合理”的流量被删掉。 一种实现:对上次dfs时找到的流量路径上的边,添 加一条“反向”边,反向边上的容量等于上次dfs时 找到的该边上的流量,然后再利用“反向”的容量和 其他边上剩余的容量寻找路径。
已经证明这种算法的复杂度上限为nm2 (n是 点数,m是边数)
Poj 1273 Drainage Ditches
赤裸裸的网络流题目。给定点数,边数,每条 边的容量,以及源点,汇点,求最大流。
Sample Input Sample Output 54 1 2 40 1 4 20 2 4 20 2 3 30 3 4 10
#define INFINITE 999999999 //Poj 1273 Drainage Ditches 的 Dinic算法 int G[300][300]; bool Visited[300]; int Layer[300]; int n,m; //1是源点,m是汇点 bool CountLayer() { int layer = 0; deque<int>q; memset(Layer,0xff,sizeof(Layer));//都初始化成-1 Layer[1] = 0; q.push_back(1); while( ! q.empty()) { int v = q.front(); q.pop_front(); for( int j = 1; j <= m; j ++ ) { if( G[v][j] > 0 && Layer[j] == -1 ) { //Layer[j] == -1 说明j还没有访问过 Layer[j] = Layer[v] + 1; if( j == m ) //分层到汇点即可 return true; else q.push_back(j); } } } return false; }
a
n
b
为什么添加反向边(取消流)是有效的?
构建残余网络时添加反向边a->b,容量是n,增 广的时候发现了流量n-k,即新增了n-k的流量。 这n-k的流量,从a进,b出,最终流到汇
a n-k
n
b
现要证明这2n-k的从流量,在原图上确实是 可以从源流到汇的。
a n-k
n
b
把流入b的边合并,看做一条,把流出a的边 也合并,同理把流入a和流出b的边也都合并。
a
100
100 100 t
第一次dfs后:
s 100
b
100
第一次dfs后,添加反向边得到的新图:
a 100 s 100
100 100
b 100
t
这样我们第二次dfs搜索的时候就可以在新的网 络里找是两条路径 的合并
a
100 s
100 b 100
100
t 100
如果回溯到源点而且无法继续往下走了,DFS结束。
因此,一次DFS过程中,可以找到多条增广路径。
DFS结束后,对残余网络再次进行分层,然后再进行DFS 当残余网络的分层操作无法算出汇点的层次(即BFS到达 不了汇点)时,算法结束,最大流求出。 一般用栈实现DFS,这样就能从栈中提取出增广路径。
Dinic 复杂度是 n*n*m (n是点数,m是边数)
while( ! q.empty()) { v = q.front(); q.pop_front(); for( i = 1;i <= m;i ++) {
if( G[v][i] > 0 && Visited[i] == 0) { //必须是依然有容量的边,才可以走 Prev[i] = v; Visited[i] = 1; if( i == m ) { bFindPath = true; q.clear(); break; } else q.push_back(i); } }
解决最大流的Ford-Fulkerson算 法
基本思路很简单,每次用dfs从源到汇找一 条可行路径, 然后把这条路塞满。这条 路径上容量最小的那条边的容量,就是这 次dfs所找到的流量。然后对于路径上的 每条边,其容量要减去刚才找到的流量。 这样,每次dfs都可能增大流量,直到某次 dfs找不到可行路径为止,最大流就求出 来了 这个想法是否正确?
int Dinic() { int i; int s; int nMaxFlow = 0; deque<int> q;//DFS用的栈 while( CountLayer() ) { //只要能分层 q.push_back(1); //源点入栈 memset(Visited,0,sizeof(Visited)); Visited[1] = 1; while( !q.empty()) { int nd = q.back(); if( nd == m ) { // nd是汇点 //在栈中找容量最小边 int nMinC = INFINITE; int nMinC_vs; //容量最小边的起点 for( i = 1;i < q.size(); i ++ ) { int vs = q[i-1]; int ve = q[i]; if( G[vs][ve] > 0 ) { if( nMinC > G[vs][ve] ) { nMinC = G[vs][ve]; nMinC_vs = vs; } } }
Dinic 算法
先利用 BFS对残余网络分层
s -1
-1
1 2
3
2
-1
1
2 3
t
2
-1
2
-1
1
3
一个节点的“层”数,就是源点到它最少要经过的边数。
1
3
-1
Dinic 算法
利用 BFS对残余网络分层,分完层后,利用DFS从前 一层向后一层反复寻找增广路。
1
1 1 2
3 2
s 0
2
t
2
2
3
3
n a n-k
n-k
n
n-k
b n
在原图上可以如下分配流量,则能有2n-k从 源流到汇点:
n a
n-k
n a n-k
n-k
k
n
n-k
b n
n-k
b n
没有严格证明,只给一个感性认识。几条反 向边相连的各种复杂情况也可以类似分析。
Ford-Fulkerson算法
求最大流的过程,就是不断找到一条源到汇 的路径,然后构建残余网络,再在残余网络 上寻找新的路径,使总流量增加,然后形成 新的残余网络,再寻找新路径…..直到某个 残余网络上找不到从源到汇的路径为止,最 大流就算出来了。 每次寻找新流量并构造新残余网络的过程, 就叫做寻找流量的“增广路径”,也叫“增 广”
50
#include <iostream> #include <queue> using namespace std; int G[300][300]; int Prev[300]; //路径上每个节点的前驱节点 bool Visited[300]; int n,m; //m是顶点数目,顶点编号从1开始 1是源,m是汇, n是 边数 unsigned Augment() { int v; int i; deque<int> q; memset(Prev,0,sizeof(Prev)); memset(Visited,0,sizeof(Visited)); Prev[1] = 0; Visited[1] = 1; q.push_back(1); bool bFindPath = false; //用bfs寻找一条源到汇的可行路径
}
if( ! bFindPath) return 0; int nMinFlow = 999999999; v = m; //寻找源到汇路径上容量最小的边,其容量就是此次增 加的总流量 while( Prev[v] ) { nMinFlow = min( nMinFlow,G[Prev[v]][v]); v = Prev[v]; } //沿此路径添加反向边,同时修改路径上每条边的容量 v = m; while( Prev[v] ) { G[Prev[v]][v] -= nMinFlow; G[v][Prev[v]] += nMinFlow; v = Prev[v]; } return nMinFlow; }
第二次dfs搜索又找到了一个流量为100的流,加 上第一次搜索得到的流量为100的流,总流量上 升到200
a
100
s 100
t
100
b
再对第二次dfs后的图添加反向边,变成:
a
100
s 100
100 t
100 b
100
在此图上再次进行dfs,已经找不到路径了,所以 流量无法再增加,最大流就是200
要求出最大流中每条边的流量,怎么办?
要求出最大流中每条边的流量,怎么办?
北京大学ACM暑期课讲义-计算几何教程共158页
北京大学ACM暑期课讲义计算几何教程
26、机遇对于有准备的头脑有特别的 亲和力 。 27、自信是人格的核心。
28、目标的坚定是性格中最必要的力 量泉源 之一, 也是成 功的利 器之一 。没有 它,天 才也会 在矛盾 无定的 迷径中 ,徒劳 无功。- -查士 德斐尔 爵士。 29、困难就是机遇。--温斯顿.丘吉 尔。 30、我奋斗,所以我快乐。--格林斯 潘。
46、我们若已接受最坏的,就再没有什么损失。——卡耐基 47、书到用时方恨少、事非经过不知难。——陆游 48、书籍把我们引入最美好的社会,使我们认识各个时代的伟大智者。——史美尔斯 49、熟读唐诗三百扎特
北京大学ACM暑期课讲义-计算几何教程
计算几何教程
计算几何的恶心之处
代码长,难写。
需要讨论各种边界情况。后面所介绍的算
法,有些对于边界情况的处理很完美,不 需要再做讨论;有些则不然,需要自行处 理边界情况。
精度误差
二维矢量
2-Dimension Vector
矢量
既有大小又有方向的量。
算法演示
2 被弹出栈,3 进栈。
算法演示
没有点被弹出栈,4 进栈。
算法演示
4 被弹出栈,5 进栈。
算法演示
5 被弹出栈,6 进栈。
算法演示
6 被弹出栈,7 进栈。
算法演示
如此直到下凸壳被找到。
算法演示
倒序进行扫描找到上凸壳。
Graham 扫描的时间复杂度
这太傻缺了。算法的瓶颈在排序,所以时
总时间复杂度 O(N2 log N)。
算法扩展
SPOJ CIRUT
求恰好被覆盖 1 次、2 次、„、N 次的面积。
算法的本质在于对“裸露”部分进行处理。
这种方法也可以用于求很多圆与很多凸多 边形的面积并——但是情况复杂得多,要 讨论的情况也多得多。
三维矢量
3-Dimension Vector
总体来说讨论比较辛苦,写起来不算很难。
精度存在比较大的问题,而且半平面交的
题目一般都容许 O(N2) 算法,所以并不是 很常用。
半平面交练习题
POJ 1279 POJ 3525 POJ 2451 POJ 3384
求多边形的核。核中的点满足其到多边形边 界上任一点之间的线段只与多边形在那一点相交。 求凸多边形最大内切圆半径。 裸的半平面交。朱泽园专门为他的算法出的 题,要求 O(N log N) 算法。 求在凸多边形内部放两个半径为 r 的圆所能 覆盖的最大面积。两圆可以重叠,但不能与多边形相交。 需要用到旋转卡壳。
北京大学ACM国际大学生程序设计竞赛课件4
1048
输入:
Follow My Logic
输入数据包含多个输入数据块.每个输入数据块包含以 下部分:
一个电路图,以上述形式表示,用只含'*'的单独一行结束. 多行01字符串,每行对应一组数据,包含26个0或1,分别对应AZ的值.用只含'*'的单独一行结束
输出:
对每组输入数据,输出对应的电路输出值. 每个结果占一行. 不同输入数据块的输出结果之间用空行隔开.
问题求解与程序设计 第三讲 模拟问题李Βιβλιοθήκη 新 2004.2 – 2004.6
内容提要
作业总结 - 1016 作业总结 - 1048 讨论 – 1207 作业 – 1207
1016 Numbers That Count
题意 count the numbers of each digit to form the new number 123321 -〉212223 1)Self-inventory 2) enter self inventory after k steps 3) enter inventory loop of length k 4) can not determined after 15 steps
Can not be classified
源程序
1016 c0400348274.txt 1016 c0400348198.txt
1048
Follow My Logic
对于一个逻辑电路和给定的输入值,计算该电路 对于一个逻辑电路和给定的输入值, 的输出值.该逻辑电路有一个或多个输入端, 的输出值.该逻辑电路有一个或多个输入端, 零 个或多个逻辑门电路,和一个输出端. 个或多个逻辑门电路,和一个输出端.本题中用 标准ASCll字符来表示逻辑电路:横竖导线分别 字符来表示逻辑电路: 标准 字符来表示逻辑电路 表示, 表示, 用'-'和'|'表示,转折点用'+'表示,输入端用 和 表示 转折点用' 表示 大写字母' 表示, 表示, 大写字母'A'-'Z'表示,输出端用问号'?'表示, 表示 输出端用问号' 表示 小写字母' 表示取反 与门, 表示取反. 小写字母'o'表示取反.与门,或门及电路各部 分示例如下: 分示例如下:
北大ACM训练课程点
ACM/ICPC竞赛中用到的大量算法,包括:组合数学、数论、图论、计算几何、高级数据结构等。
北京大学
课程内容共八个专题,除理论知识外还包括精选例题讲解:
7.14 数据结构(一): 线段树,树状数组,二维线段树
7.15 数学题:组合数学,数论等
7.16 数据结构(二): 并查集, DFA, Trie图等
7.17 若干图论问题:最小生成树最短路强连通分量、桥和割点等
7.21 计算几何:线与线求交,线与面求交,求凸包,半平面求交等
7.22 搜索:深搜,广搜,剪枝,A*算法
7.24 动态规划
7.24 网络流算法:基本的网络流算法,Dinic算法,带上下界的网络流,最小费用
流。
ACM暑期培训资料(ppt28张)
大整数除法
1、链接地址
/problem?id=2737
2、问题描述
– 求两个大的正整数相除的商
输入数据
第1 行是测试数据的组数n,每组测试数据占2 行,第1 行是被除数,第2 行是除数。每组测试数据之间有一个空 行,每行数据不超过100 个字符
输出要求
n 行,每组测试数据有一行输出是相应的整数商
规律:一个数的第i 位和另一个数的第j 位相乘所得的数,一 定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从 0 开始数。
POJ2389 参考程序
#include<iostream> #include<string> using namespace std; const int MAXLEN=200+10; int a[MAXLEN],b[MAXLEN]; int c[2*MAXLEN]; string st1,st2; int i,j,k; //字符串s转换为整型数组t void tran(string s,int *t) { int m,l; l=s.length(); for(m=0;m<l;m++) t[m]=s[l-1-m]-'0'; }
//下面判断p1 是否比p2 大,如果不是,返回-1 if( nLen1 == nLen2 ) { for( i = nLen1-1; i >= 0; i -- ) { if( p1[i] > p2[i] ) break; //p1>p2 else if( p1[i] < p2[i] ) return -1; //p1<p2 } } for( i = 0; i < nLen1; i ++ ) { //要求调用本函数确保当i>=nLen2 时,p2[i] = 0 p1[i] -= p2[i]; if( p1[i] < 0 ) { p1[i]+=10; p1[i+1] --; } } for( i = nLen1 -1 ; i >= 0 ; i-- ) if( p1[i] )//找到最高位第一个不为0 return i + 1; return 0;//全部为0,说明两者相等 }
ACM暑期培训课程图论.ppt
E问题
OUTPUT FORMAT
输出应当有F+1行,每行一个整数,依次表示路径经 过的顶点号。注意数据可能有多组解,但是只有上面题目 要求的那一组解是认为正确的。
SAMPLE OUTPUT(fence.out) 1 2 3 4 2 5 4 6 5 7
E问题
很显然,这是一个欧拉路径问题,我们要做的就是读入栅栏的 构图后,找到图中的一条欧拉路径。
图的连通性
4. 有向图的强连通分支
在下面的几页中,我们可以看到求图的 强连通分支的实例。
首先,图(a)为有向图G,其中的阴影部 分是G的强连通分支,在对图G进行DFS 的过程中,我们对每个顶点都标出了其 开始搜索时刻preOrder与完成时刻 postOrder,黑色边为DFS搜索树树枝;
可以看到,图G共有 4个强连通分支:
{a,b,e}
{c,d}
{f,g}
{h}
图的连通性
4. 有向图的强连通分量
(b)图中G的转置图G*。图中说明了求 强连通分支算法第3部计算出的深度优 先树,其中黑色边是树枝。每个强连通
子图对应于一棵深度优先树。图中黑色 顶点b,c,g和h是强连通子图中每个顶点 的祖先,这些顶点也是对G*进行深度 优先搜索所产生的深度优先树的树根。
int map[MAXV][MAXV]; //map[i][j]记录顶点i和顶点j之 间的路径数目
int deg[MAXV]; int path[MAXE]; 径
//deg[i]记录顶点i的度数 //path数组用来存放找到的欧拉路
int fn,minv,maxv,pathnum=0; //minv为顶点最小编号,maxv为顶点最大编号
G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。
图论讲义第9章-网络流理论
注:网络 N 的一个割 K 称为最小割,如果网络 N 中不存在割 K′使得 CapK ′ < CapK 。 推论 9.1.1 设 f * 是网络 N 的一个最大流,K*是 N 的一个最小割,则 Val f * ≤ CapK * . 证明显然。 推论 9.1.2 设 f 是 N 的一个可行流,K 是 N 的一个割,若 Val f = CapK ,则 f 是最大流而
a∈K
−
(S ) =
a∈( S , S )
∑
f (a ) ≥ 0 。
由引理 9.1.1, Val f = f + ( S ) − f − ( S ) ≤ CapK , 可见第 一个结论成立。另外注意到 f + ( S ) = CapK 当且仅当
S K
S
( S , S ) 中每条弧都是 f 饱和的;而 f − ( S ) = 0 当且仅当 ( S , S ) 中每条弧都是 f 零的,故定理的第二个结论也成立。证毕。
v2 v1 x P Q v3 y v6 v4 v5
定义 9.2.2 设 f 是网络 N = (V , x, y , A, C ) 中的一可行流,P 是 N 中一条 x-y 路。如果对于 P 上任一条弧
a ,都有
(1) 若弧 a 是 P 的正向弧,则 Δf ( a ) (2) 若弧 a 是 P 的反向弧,则 Δf ( a )
Байду номын сангаас
§9.1 网络与网络流的基本概念
定义 9.1.1 一个网络 N=(V,A)是指一个连通无环弧且满足下列条件的有向图: (1) 有一个顶点子集 X,其每个顶点的入度都为 0; (2) 有一个与 X 不相交的顶点子集 Y,其每个顶点的出度都为 0; (3) 每条弧都有一个非负的权,称为弧的容量。 注: 上述网络 N 可写作 N=(V, X, Y, A, C),X 称为网络的发点集或源点集,Y 称为网络的 收点集或汇点集,C 称为网络的容量函数。 例:
北京大学ACM暑期课讲义-lcc-博弈
• 例:取石子问题
– 有一堆n个石子 两人轮流取 每次可以取1..m个 没得取的判负 在游戏中,n不断变化,m是定值 一个局面可以用当前的石子数n来表示 n = 0 必败 n = 1..m都是必胜局面 n = m+1 是必败局面(为什么?) n = m+2.. 2m+1 是必胜局面(为什么?)
– 附加问题:先手必胜时,输出当前一步可选 步
• 最终局面都是P局面 • 对于一个局面,若至少有一种操作使它变为P局 面,则它是N局面
– 如果当前是我的回合,我有一种操作选择,使得下 回合对方面对的局面是个必输局面,那么我的局面 就是必胜局面 – 问题:如何寻找这一步操作选择?(输出解)
• 对于一个局面,无论如何操作都使它变为N局面, 则它是P局面
• 双方每步可以从并行存在的多个子游戏中选择 一个操作
– 维度可以进一步变得更多 – 可能存在一个子游戏,由同一个玩家连续两次对其 操作
• 对sg函数来说:怎么变都无所谓,它只关心所 有子局面的异或值
– Sg(x)的变量x永远是一维的。
竞赛中的博弈题策略
• 利用最简单的N-P图染色来判定 • 博弈搜索:用搜索方式来对图染色
• 同样可以用拓扑序对节点计算sg值
SG函数与NP局面的关系
• 初始节点(必须是P节点)没有出边, sg = 0 • 若v有边指向某sg(vi) = 0的vi,则sg(v)>0 • 若v没有边指向某sg(vi) = 0的vi,则sg(v) =0
• 所有P节点sg = 0 • 所有N节点sg > 0
– 可以看做α-β剪枝的特例
acm知识点
要注意,ACM的竞赛性强,因此自己应该和自己的实际应用联系起来.
适合自己的才是好的,有的人不适合搞算法,喜欢系统架构,因此不要看到别人什么就眼红,
发挥自己的长处,这才是重要的.
第一阶段:练经典常用算法,下面的每个算法给我打上十到二十遍,同时自己精简代码,
STL中的数据结构
vector
deque
set / map
动态规划 / 记忆化搜索
动态规划和记忆化搜索在思考方式上的区别
最长子序列系列问题
最长不下降子序列
最长公共子序列
最长公共不下降子序列
快速数论变换
……
素数问题
概率判素算法
概率因子分解
数据结构
组织结构
二叉堆
左偏树
二项树
胜者树
跳跃表
样式图标
弦图的性质和判定
组合数学
解决组合数学问题时常用的思想
逼近
递推 / 动态规划
概率问题
Polya定理
计算几何 / 解析几何
计算几何的核心:叉积 / 面积
解析几何的主力:复数
基本形
因为太常用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都可以把程序打
出来.
1.最短路(Floyd、Dijstra,BellmanFord)
2.最小生成树(先写个prim,kruscal要用并查集,不好写)
3.大数(高精度)加减乘除
4.二分查找. (代码可在五行以内)
5.叉乘、判线段相交、然后写个凸包.
(3)简单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)
acm-icpc暑期课_最短路
acm-icpc暑期课_最短路北京⼤学暑期课《ACM/ICPC 竞赛训练》最短路算法本讲义为四处抄袭后改编⽽成,来源已不可考。
仅⽤于内部授课北京⼤学信息学院郭炜 guo_wei@/doc/d83c4f84bb68a98270fefa4a.html/doc/d83c4f84bb68a98270fefa4a.html /guoweiofpkuDijkstra 算法●解决⽆负权边的带权有向图或⽆向图的单源最短路问题●贪⼼思想,若离源点s 前k-1近的点已经被确定,构成点集P ,那么从s 到离s 第k 近的点t 的最短路径,{s,p 1,p 2…p i ,t}满⾜s,p 1,p 2…p i ∈P 。
●否则假设pi ?P ,则因为边权⾮负,pi 到t 的路径≥0,则d[pi]≤d[t],pi 才是第k 近。
将pi 看作t ,重复上⾯过程,最终⼀定会有找不到pi 的情况●d[i]=min(d[p i ]+cost(p i ,i)),i ?P,p i ∈P d[t]=min(d[i]) ,i ?P基本思想●初始令d[s]=0,d[i]=+∞,P=?●找到点i?P,且d[i]最⼩●把i添⼊P,对于任意j?P,若d[i]+cost(i,j)d[j]=d[i]+cost(i,j)。
●⽤邻接表,不优化,时间复杂度O(V2+E)●Dijkstra+堆的时间复杂度 o(ElgV)●⽤斐波那契堆可以做到O(VlogV+E)●若要输出路径,则设置prev数组记录每个节点的前趋点,在d[i]更新时更新prev[i]4 23 150200200 100080005000v Dist[v]0 01234 50100001000300025025082505250源点0加⼊P后:Dijkstra's AlgorithmDijkstra 算法也适⽤于⽆向图。
但不适⽤于有负权边的图。
231-234d[1,2] = 2但⽤Dijkstra 算法求得 d[1,2] = 3有N个孩⼦(N<=3000)分糖果。
