OJ-Problems-Source/.ACM-Templates/Graph/图论-生成树.cpp

290 lines
9.6 KiB
C++
Raw Normal View History

2016-08-13 23:07:20 +08:00
//最小生成树
//Prim + 邻接矩阵 O(V^2)
typedef int wtype;
const wtype INF = 0x3f3f3f3f;
int n; wtype mp[N][N], cost[N];
bool vis[N];
wtype Prim(int src) {
memset(vis, 0, sizeof(vis)); memcpy(cost, mp[src], sizeof(mp[src]));
wtype ret = 0; vis[src] = true;
for (int j = 1; j < n; j++) {
int u = -1; wtype mn = INF;
for (int i = 0; i < n; i++) {
if (!vis[i] && mn > cost[i]) { mn = cost[i]; u = i; }
}
if (mn == INF) { return -1; } //原图不连通
vis[u] = true; ret += mn;
for (int v = 0; v < n; v++) {
if (!vis[v] && cost[v] > mp[u][v]) { cost[v] = mp[u][v]; }
}
}
return ret;
}
//Prim + priority_queue + 邻接表 O(ElogV)
typedef int wtype;
int head[N], to[M], nxt[M], tot; wtype len[M], cost[N];
bool vis[N];
void init() { tot = 0; memset(head, -1, sizeof(head)); }
void addedge(int x, int y, wtype z) { to[tot] = y; len[tot] = z; nxt[tot] = head[x]; head[x] = tot++; }
struct Node {
int v; wtype w;
bool operator<(const Node &r)const { return w > r.w; }
};
wtype Prim(int src) {
memset(cost, 0x3f, sizeof(cost)); memset(vis, 0, sizeof(vis));
wtype ret = 0;
priority_queue<Node> que; que.push((Node) {src, cost[src] = 0});
while (!que.empty()) {
int u = que.top().v; que.pop();
if (vis[u]) { continue; }
vis[u] = true; ret += cost[u];
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (!vis[v] && cost[v] > len[i]) { cost[v] = len[i]; que.push((Node) {v, cost[v]}); }
}
}
return ret;
}
//Kruskal + 邻接表 O(ElogE)
typedef int wtype;
struct Edge {
int u, v; wtype w;
bool operator<(const Edge &r)const { return w < r.w; }
} edge[M];
int n, fa[N], tot; //加边前赋值为0
void addedge(int u, int v, wtype w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
wtype Kruskal() {
memset(fa, -1, sizeof(fa));
sort(edge, edge + tot);
int cnt = 0; wtype ret = 0;
for (int i = 0; i < tot; i++) {
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
if (t1 != t2) { ret += w; fa[t1] = t2; cnt++; }
if (cnt == n - 1) { break; }
}
if (cnt < n - 1) { return -1; } //不连通
return ret;
}
//次小生成树
//Prim + 邻接矩阵 O(V^2 + E)
//求最小生成树时, 用数组Mx[i][j]来表示MST中i到j最大边权
//求完后, 直接枚举所有不在MST中的边, 替换掉最大边权的边, 更新答案
typedef int wtype;
const wtype INF = 0x3f3f3f3f;
int n, pre[N];
wtype mp[N][N], cost[N], Mx[N][N]; //Mx[i][j]表示在最小生成树中从i到j的路径中的最大边权
bool vis[N], used[N][N];
wtype Prim() {
memset(Mx, 0, sizeof(Mx));
memset(pre, 0, sizeof(pre));
memset(vis, 0, sizeof(vis));
memset(used, 0, sizeof(used));
wtype ret = 0; vis[0] = true; pre[0] = -1;
for (int i = 1; i < n; i++) { cost[i] = mp[0][i]; }
for (int j = 1; j < n; j++) {
int u = -1; wtype mn = INF;
for (int j = 0; j < n; j++)
if (!vis[j] && mn > cost[j]) { mn = cost[j]; u = j; }
if (mn == INF) { return -1; } //原图不连通
vis[u] = true; ret += mn;
used[u][pre[u]] = used[pre[u]][u] = true;
for (int v = 0; v < n; v++) {
if (vis[v]) { Mx[v][u] = Mx[u][v] = max(Mx[v][pre[u]], cost[u]); }
if (!vis[v] && cost[v] > mp[u][v]) { cost[v] = mp[u][v]; pre[v] = u; }
}
}
return ret;
}
//Kruskal + 邻接表 O(VElogE)
typedef int wtype;
const wtype INF = 0x3f3f3f3f;
struct Edge {
int u, v; wtype w;
bool operator<(const Edge &r)const { return w < r.w; }
} edge[M];
int n, fa[N], path[N], tot; //加边前赋值为0
void addedge(int u, int v, wtype w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
wtype Kruskal() {
memset(fa, -1, sizeof(fa));
sort(edge, edge + tot);
int cnt = 0; wtype ret = 0;
for (int i = 0; i < tot; i++) {
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
if (t1 != t2) { ret += w; fa[t1] = t2; path[cnt++] = i; }
if (cnt == n - 1) { break; }
}
if (cnt < n - 1) { return -1; } //不连通
return ret;
}
wtype KruskalSec() {
wtype ret = INF;
for (int x = 0; x < n - 1; x++) {
memset(fa, -1, sizeof(fa));
int cnt = 0; wtype tmp = 0;
for (int i = 0; i < tot; i++) {
if (i != path[x]) {
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
if (t1 != t2) { tmp += w; fa[t1] = t2; cnt++; }
if (cnt == n - 1) { if (tmp < ret) { ret = tmp; } break; }
}
}
}
if (ret == INF) { return -1; } //不存在
return ret;
}
//最小树形图
//朱刘算法 O(VE)
typedef int wtype;
const int N = 1005;
const int M = 40005;
const wtype INF = 0x3f3f3f3f;
struct Edge { int u, v; wtype w; } edge[M];
int n, m, pre[N], id[N], vis[N];
wtype g[N][N], in[N];
wtype Zhuliu(int root) {
wtype res = 0; int u, v;
for (;;) {
memset(in, 0x3f, sizeof(in));
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
for (int i = 0; i < m; i++) {
if (edge[i].u != edge[i].v && edge[i].w < in[edge[i].v]) {
pre[edge[i].v] = edge[i].u; in[edge[i].v] = edge[i].w;
}
}
for (int i = 0; i < n; i++) {
if (i != root && in[i] == INF) { return -1; } //不存在最小树形图
}
int tn = 0; in[root] = 0;
for (int i = 0; i < n; i++) {
res += in[i]; 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] = tn; }
id[v] = tn++;
}
}
if (tn == 0) { break; } //没有有向环
for (int i = 0; i < n; i++) {
if (id[i] == -1) { id[i] = tn++; }
}
for (int i = 0; i < m;) {
v = edge[i].v; edge[i].u = id[edge[i].u]; edge[i].v = id[edge[i].v];
if (edge[i].u != edge[i].v) { edge[i++].w -= in[v]; }
else { swap(edge[i], edge[--m]); }
}
n = tn; root = id[root];
}
return res;
}
//POJ 3164
int main() {
int C = 0, T, u, v, w;
scanf("%d", &T);
while (++C <= T) {
memset(g, 0x3f, sizeof(g));
scanf("%d%d", &n, &m);
while (m--) {
scanf("%d%d%d", &u, &v, &w);
if (u == v) { continue; }
g[u][v] = min(g[u][v], w);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (g[i][j] < INF) { edge[m].u = i; edge[m].v = j; edge[m++].w = g[i][j]; }
}
}
wtype ans = Zhuliu(0);
printf("Case #%d: ", C);
if (ans == -1) { puts("Possums!"); }
else { printf("%d\n", ans); }
}
}
//曼哈顿距离最小生成树
//Kruskal O(VlogV)
const int INF = 0x3f3f3f3f;
struct Point {
int x, y, id;
bool operator<(const Point &r)const { return x < r.x || (x == r.x && y < r.y); }
} p[N];
struct Edge { //有效边
int u, v, w;
bool operator<(const Edge &r)const { return w < r.w; }
} edge[N << 2];
struct BIT { //树状数组, 找y-x大于当前的, 但是y+x最小的
int min_val, pos;
void init() { min_val = INF; pos = -1; }
} bit[N];
int n, tot, fa[N];
int a[N], b[N];
void addedge(int u, int v, int w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
inline int cost(Point a, Point b) { return abs(a.x - b.x) + abs(a.y - b.y); }
inline int lowbit(int x) { return x & (-x); }
void update(int i, int val, int pos) {
for (; i > 0; i -= lowbit(i)) {
if (val < bit[i].min_val) { bit[i].min_val = val; bit[i].pos = pos; }
}
}
int query(int i, int m) { //查询[i, m]的最小值位置
int min_val = INF, pos = -1;
for (; i <= m; i += lowbit(i)) {
if (bit[i].min_val < min_val) { min_val = bit[i].min_val; pos = bit[i].pos; }
}
return pos;
}
void MMST() {
tot = 0;
for (int d = 0; d < 4; d++) { //4种坐标变换
if (d == 1 || d == 3) { for (int i = 0; i < n; i++) { swap(p[i].x, p[i].y); } }
else if (d == 2) { for (int i = 0; i < n; i++) { p[i].x = -p[i].x; } }
sort(p, p + n);
for (int i = 0; i < n; i++) { a[i] = b[i] = p[i].y - p[i].x; }
sort(b, b + n);
int m = unique(b, b + n) - b;
for (int i = 1; i <= m; i++) { bit[i].init(); }
for (int i = n - 1 ; i >= 0; i--) {
int pos = lower_bound(b, b + m, a[i]) - b + 1, ans = query(pos, m);
if (ans != -1) { addedge(p[i].id, p[ans].id, cost(p[i], p[ans])); }
update(pos, p[i].x + p[i].y, i);
}
}
}
int Kruskal() {
MMST();
memset(fa, -1, sizeof(fa));
sort(edge, edge + tot);
int ret = 0;
for (int i = 0, k = 0; i < tot; i++) {
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v);
if (t1 != t2) {
fa[t1] = t2; ret += edge[i].w;
if (++k == n - 1) { return ret; }
}
}
}
//POJ3241 求曼哈顿最小生成树上第k大的边
int Kruskal(int k) {
MMST(n, p);
memset(fa, -1, sizeof(fa));
sort(edge, edge + tot);
for (int i = 0; i < tot; i++) {
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v);
if (t1 != t2) {
fa[t1] = t2;
if (--k == 0) { return edge[i].w; }
}
}
}
//生成树计数
//Matrix-Tree定理(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]表示。