黑书上的例题,具体模型是求一个无向图的最小生成树,其中有一个点的度有限制(假设为 k)。
要求最小 k 度生成树,我们可以按照下面的步骤来做:
设有度限制的点为 V0 ,V0称为根节点
1,把所有与 V0 相连的边删去,图会分成多个子图(假设为 m 个,显然的,如果 m > k,那么问题无解),让他们分别求最小生成树;然后用最小的代价将 m 个最小生成树和 V0 连起来,那我们就得到了一棵关于 V0 的最小 m 度生成树。
2,在 m 度生成树中找一个点和 V0 相连(设这条边的权值为 a),会生成一个环,为了满足最小生成树的要求,我们必须删掉一条边(设这条边的权值为 b),以使总权值尽量小,那么就要求 a 尽量的小,b 尽量的大。
完成一次 2 的操作后得到的是 m+1 度最小生成树,以此类推,直到得到最小 k 度生成树。
PS:这道题并不是要求 k 度的最小生成树,而是要求根节点的度在不超过 k 值的情况下,该图的最小生成树。也就是说,不一定要求到 k 度生成树,只要图的总权值不能继续减小我们就可以停下来了。
代码是“拿来”的,因为用map处理数据,所以代码量小了一点,而且方便的多了,STL是真的很强大,orz……
/******************************************************************************************************
** Copyright (C) 2011.07.01-2013.07.01
** Author: famousDT <13730828587@163.com>
** Edit date: 2011-12-20
******************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>//abs,atof(string to float),atoi,atol,atoll
#include <math.h>//atan,acos,asin,atan2(a,b)(a/b atan),ceil,floor,cos,exp(x)(e^x),fabs,log(for E),log10
#include <vector>
#include <queue>
#include <map>
#include <time.h>
#include <set>
#include <list>
#include <stack>
#include <string>
#include <iostream>
#include <fstream>
#include <assert.h>
#include <bitset>
#include <iterator>//C++Primer
#include <string.h>//memcpy(to,from,count
#include <ctype.h>//character process:isalpha,isdigit,islower,tolower,isblank,iscntrl,isprll
#include <numeric>
#include <functional>
#include <algorithm>
using namespace std;
typedef long long int ll;
#define MY_PI acos(-1)
#define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
#define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
#define MY_MALLOC(n, type) ((type *)malloc((n) * sizeof(type)))
#define MY_ABS(a) (((a) >= 0) ? (a) : (-(a)))
#define MY_INT_MAX 0x7fffffff
/*==========================================================*\
|
\*==========================================================*/
const int N = 21;
struct node {
int v, cost;//点v, dist[v]
node() {}
node(int x, int y) : v(x), cost(y) {}
};
bool operator < (const node &l, const node &r)
{
return l.cost > r.cost;
}
priority_queue<node> q;
map<string, int> Map;
int mat[N][N], dist[N], clo[N], pre[N], fst[N], max_side[N];
int n, K;
int prim(int s, int id)
{
while (!q.empty()) q.pop();
dist[s] = 0;
q.push(node(s, 0));
int i, res = 0;
while (!q.empty()) {
node cur = q.top();
q.pop();
int u = cur.v;
if (!clo[u]) {
clo[u] = id;
res += dist[u];
for (i = 1; i < n; ++i) {
if (!clo[i] && mat[u][i] && mat[u][i] < dist[i]) {//满足松弛条件
pre[i] = u;
dist[i] = mat[u][i];
q.push(node(i, dist[i]));
}
}
}
}
return res;
}
void update(int cur, int last, int maxside)
//也是一个dfs过程,直到搜回到起点,同时完成了max_side[]更新
{
max_side[cur] = maxside > mat[cur][last] ? maxside : mat[cur][last];
int i;
for (i = 1; i < n; ++i) {
if (last != i && mat[cur][i] && (pre[cur] == i || pre[i] == cur))
update(i, cur, max_side[cur]);
}
}
void solve()
{
int i, res, cnt;
for (i = 0; i < n; ++i) {
dist[i] = MY_INT_MAX;
clo[i] = pre[i] = fst[i] = 0;
}
res = 0;
cnt = 1;//除去根节点后,图中的连通子图个数,即最小生成树个数
for (i = 1; i < n; ++i) {
if (!clo[i]) {
res += prim(i, cnt++);
}
}
for (i = 1; i < n; ++i) {//找到每个生成树和Park最近的点使之和Park相连
int id = clo[i];
if (mat[0][i] && (!fst[id] || mat[0][i] < mat[0][fst[id]]))
fst[id] = i;
}
for (i = 1; i < cnt; ++i) {//把m个生成树上和根节点相连的边加入res,得到关于Park的最小m度生成树
res += mat[0][fst[i]];
mat[0][fst[i]] = mat[fst[i]][0] = 0;//之所以用邻接阵就是因为删除边很方便
update(fst[i], 0, 0);
}
/* 添删操作:将根节点和生成树中一个点相连,会产生一个环,将这个环上(除刚添的那条边外)权值最大
的边删去.由于每次操作都会给总权值带来影响d=max_side[tmp]-mat[0][tmp],我们需要得到最小生
成树,所以我们就要求 d 尽量大
*/
K = K - cnt + 1;//接下来重复操作,直到度数满足条件
while (K--) {
int tmp = 0;
for (i = 1; i < n; ++i) {//找d值最大的点(就是说完成添删操作后可以使总边权减小的值最大)
if (mat[0][i] && (tmp == 0 || max_side[tmp] - mat[0][tmp] < max_side[i] - mat[0][i]))
tmp = i;
}
if (max_side[tmp] <= mat[0][tmp]) break;//总权值无法再减小
res -= max_side[tmp] - mat[0][tmp];
mat[0][tmp] = mat[tmp][0] = 0;
int p = 0;
for (i = tmp; pre[i]; i = pre[i])
if (p == 0 || mat[p][pre[p]] < mat[i][pre[i]])
p = i;
pre[p] = 0;
update(tmp, 0, 0);
}
printf("Total miles driven: %d\n", res);
}
int main()
{
int m, weight;
string a, b;
while (~scanf("%d", &m)) {
Map["Park"] = 0;
n = 1;
memset(mat, 0, sizeof(mat));
while (m--) {
cin>>a>>b>>weight;
if (!Map.count(a)) Map[a] = n++;
if (!Map.count(b)) Map[b] = n++;
int u = Map[a];
int v = Map[b];
if (!mat[u][v] || weight < mat[u][v])
mat[u][v] = mat[v][u] = weight;
}
scanf("%d", &K);
solve();
}
return 0;
}