第一个部分 前4题 次小生成树
算法:首先如果生成了最小生成树,那么这些树上的所有的边都进行标记。标记为树边。
接下来进行枚举,枚举任意一条不在MST上的边,如果加入这条边,那么肯定会在这棵树上形成一个环,如果还要维护处树的特点
那么就要在这个环上删去一条边,这样他还是树,删掉的边显然是这条链上权值最大边更可能形成次小生成树。那么就有2中方法可以做。
第一种PRIM在prim时候直接可以做出这个从I到J的链上权值最大的值MAX[i][j];
同时可以用kruskal同样方式标记树边,然后DFS跑出MAX[i][j]
其中prim适合稠密图,kruskal适合稀疏图
2份模板
第一份:prim
#include
View Code
第二份:kruskal
#include
View Code
POJ 1679 The Unique MST
#include
View Code
HDU 4081 Qin Shi Huang's National Road System
这个题的关键边可以是树边可以非树边主要需要MAX[i][j]数组
#include
View Code
HDU 10600 ACM Contest and Blackout
#include
View Code
UVA 10462 Is There A Second Way Left?
#include
View Code
___________________________________________________________________________________________________________________________________________________
下面是最小树形图。
一些很好的资料
const int MAXN = 1010;
const int MAXM = MAXN * MAXN;
const int INF = 0x3f3f3f3f;
typedef int type;
struct Edge
{
int u,v;
type w;
}edge[MAXM];
int pre[MAXN], id[MAXN], vis[MAXN], N,M;
type in[MAXN];
type Zhuliu(int root, int V, int E)
{
type ret = 0;
while(true)
{
//1.找最小入边
for(int i = 0; i < V; i++) in[i] = INF;
for(int i = 0; i < E; i++)
{
int u = edge[i].u;
int v = edge[i].v;
if(edge[i].w < in[v] && u != v) {pre[v] = u; in[v] = edge[i].w;}
}
for(int i = 0; i < V; i++)
{
if(i == root) continue;
if(in[i] == INF) return -1;//除了跟以外有点没有入边,则根无法到达它
}
//2.找环
int cnt = 0;
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
in[root] = 0;
for(int i = 0; i < V; i++) //标记每个环
{
ret += in[i];
int v = i;
while(vis[v] != i && id[v] == -1 && v != root) //每个点寻找其前序点,要么最终寻找至根部,要么找到一个环
{
vis[v] = i;
v = pre[v];
}
if(v != root && id[v] == -1)//缩点
{
for(int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;
id[v] = cnt++;
}
}
if(cnt == 0) break; //无环 则break
for(int i = 0; i < V; i++)
if(id[i] == -1) id[i] = cnt++;
//3.建立新图
for(int i = 0; i < E; i++)
{
int u = edge[i].u;
int v = edge[i].v;
edge[i].u = id[u];
edge[i].v = id[v];
if(id[u] != id[v]) edge[i].w -= in[v];
}
V = cnt;
root = id[root];
}
return ret;
}
View Code
#include
View Code
UVA 11183 Teen Girl Squad
#include
View Code
HDU 2121 Ice_cream’s world II
非固定根的最小树形图,这里添加虚拟根,连接各个顶点。权值要足够大,这里权值就设置所有输入边权值和+1。这个值为sum
如果最后最小树形图代价大于等于2*sum就说明利用2条虚拟边作为树边这种情况就是最小树形图不存在
另外注意还要输出最优的那个根,这里要根据边的关系来做。因为在zhuliu算法中每次缩点后都要给所有点从新编号,对于这个题输出答案就很麻烦,
所以就从边上下手;
#include
View Code
HDU 4009 Transfer water
#include
View Code
————————————----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
生成树计数
Matrix-Tree定理(Kirchhoff矩阵-树定理)。Matrix-Tree定理是解决生成树计数问题最有力的武器之一。它首先于1847年被Kirchhoff证明。在介绍定理之前,我们首先明确几个概念:
1、G的度数矩阵D[G]是一个n*n的矩阵,并且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数。
2、G的邻接矩阵A[G]也是一个n*n的矩阵, 并且满足:如果vi、vj之间有边直接相连,则aij=1,否则为0。
我们定义G的Kirchhoff矩阵(也称为拉普拉斯算子)C[G]为C[G]=D[G]-A[G],则Matrix-Tree定理可以描述为:G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值。所谓n-1阶主子式,就是对于r(1≤r≤n),将C[G]的第r行、第r列同时去掉后得到的新矩阵,用Cr[G]表示。
模板:
#include
View Code
UVA 10766 Organising the Organisation
#include
View Code
SPOJ DETER3 DETER3 - Find The Determinant III
计算行列式
#include
View Code
URAL 1627 Join
#include
View Code
HDU 4305 Lighting
这个题处理很麻烦。计算几何完全不会。。看了bin神代码写的。不过思路很简单。
#include
View Code
SPOJ HIGH HIGH - Highways
#include
View Code
HDU 4408 Minimum Spanning Tree
最小生成树计数。暂时没看太明白
贤贴在这里
生成树计数可以使用Matrix-Tree定理解决,本题最主要的区别是有了一个最小生成树的额外条件。
首先考虑一下如何得到最小生成树。
Kruskal算法的基本思想是,按照边长排序,然后不断将短边加入集合,最终一步如果能成功把n-1条边都加入同一个集合,则找到了最小生成树。在维护集合时,可以使用并查集来快速处理。
如果把Kruskal的过程按照边长划分成多个阶段,实际上是处理了所有短边的连通性之后继续处理下一个长度的边的连通性,并依次继续处理剩下边的连通性。然后我们可以发现,不同长度的边之间的连通性互不影响!!!
假设存在n1条长度为c1的边,n2条长度为c2的边...则Kruskal首先处理c1边的连通性,然后处理c2边的连通性,对于c1边的连通性的处理可能有多种方案,即从n1条边中取出一定数量的边构成最大连通图,但是最终处理完之后的结果对于c2来说是完全一样的。因此算法就出来了,在Kruskal的基础上,使用Matrix-Tree定理处理每个阶段生成树的种数,最后将所有阶段的结果相乘即可。
/*
*算法引入:
*给定一个含有N个结点M条边的无向图,求它最小生成树的个数t(G);
*
*算法思想:
*抛开“最小”的限制不看,如果只要求求出所有生成树的个数,是可以利用Matrix-Tree定理解决的;
*Matrix-Tree定理此定理利用图的Kirchhoff矩阵,可以在O(N3)时间内求出生成树的个数;
*
*kruskal算法:
*将图G={V,E}中的所有边按照长度由小到大进行排序,等长的边可以按照任意顺序;
*初始化图G’为{V,Ø},从前向后扫描排序后的边,如果扫描到的边e在G’中连接了两个相异的连通块,则将它插入G’中;
*最后得到的图G’就是图G的最小生成树;
*
*由于kruskal按照任意顺序对等长的边进行排序,则应该将所有长度为L0的边的处理当作一个阶段来整体看待;
*令kruskal处理完这一个阶段后得到的图为G0,如果按照不同的顺序对等长的边进行排序,得到的G0也是不同;
*虽然G0可以随排序方式的不同而不同,但它们的连通性都是一样的,都和F0的连通性相同(F0表示插入所有长度为L0的边后形成的图);
*
*在kruskal算法中的任意时刻,并不需要关注G’的具体形态,而只要关注各个点的连通性如何(一般是用并查集表示);
*所以只要在扫描进行完第一阶段后点的连通性和F0相同,且是通过最小代价到达这一状态的,接下去都能找到最小生成树;
*
*经过上面的分析,可以看出第一个阶段和后面的工作是完全独立的;
*第一阶段需要完成的任务是使G0的连通性和F0一样,且只能使用最小的代价;
*计算出这一阶段的方案数,再乘上完成后续事情的方案数,就是最终答案;
*
*由于在第一个阶段中,选出的边数是一定的,所有边的长又都为L0;
*所以无论第一个阶段如何进行代价都是一样的,那么只需要计算方案数就行了;
*此时Matrix-Tree定理就可以派上用场了,只需对F0中的每一个连通块求生成树个数再相乘即可;
*
*Matrix-Tree定理:
*G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值;
*n-1阶主子式就是对于r(1≤r≤n),将C[G]的第r行,第r列同时去掉后得到的新矩阵,用Cr[G]表示;
*
*算法举例:
*HDU4408(Minimum Spanning Tree)
*
*题目地址:
*http://acm.hdu.edu.cn/showproblem.php?pid=4408
*
*题目大意:
*给定一个含有N个结点M条边的无向图,求它最小生成树的个数,所得结果对p取模;
**/
#include
View Code