@DATASOURCE
2015-09-04T17:39:04.000000Z
字数 3438
阅读 1778
图论
生成树
最小度限制生成树就是给一个图,求它的满足点vo的度数最大为k的最小生成树.
由上述步骤可知,由 m 度限制生成树拓展为 m + 1 度限制生成树的过程中需要大量的重复运算, 可以运用动态规划来优化。
设 dp(v) 为路径 v0—v 上与 v0 无关联且权值最大的边。定义 father(v) 为 v 的父结点,动态转移方程
并查集 + kruskal;
首先,每个连通分量的的最小生成树可以直接用一个循环,循环着 Kruskal 求出;
这里利用了联通分量间的独立性,对每个连通分量分别求最小生成树,和放在一起求,毫不影响;
而且 kruskral 算法保证了各连通分量边的有序性;
找最小边的时候,可以用动态规划,也可以这么做:
先走一个循环,但我们需要逆过来加边,将与 v0 关联的所有边从小到达排序;
然后将各连通分量连接起来,利用并查集可以保证每个连通分量只有一条边与 v0 相连;
由于边已经从小到达排序,故与每个连通分量相连的边就是每个连通分量与 v0 相连中的最小边;
然后求 m + 1度的最小生成树时,可以直接用DFS,最小生成树要一直求到k度,然后从中找出一个最优值;
题意:
给出m条边,每条边有两个端点和一个权值;
求这个图在满足以下条件的情况下的最小生成树:
在所有点中,有一个特殊点Park,它在求得的最小生成树中的度必须小于等于某个值;示例代码
#include <map>
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 50;
const int MAX_INT = (1 << 29);
struct Edge{
int v, w, nxt;
};
struct Node{
int u, v, w;
friend bool operator < (const Node &a, const Node &b){
return a.w < b.w;
}
};
int pcnt;
int par[MAXN];
map <string, int> ma;
Node node[MAXN * MAXN], dp[MAXN];
int ecnt, n, m, k;
bool use[MAXN][MAXN];
Edge edge[MAXN * MAXN];
int head[MAXN], vis[MAXN], dis[MAXN];
int Min[MAXN], tmp[MAXN];
void init(){
ma.clear();
ecnt = pcnt = 0;
memset(dp, -1, sizeof(dp));
memset(use, 0, sizeof(use));
memset(edge, 0, sizeof(edge));
memset(node, 0, sizeof(node));
memset(head, -1, sizeof(head));
fill(Min, Min + MAXN, MAX_INT);
for(int i = 0; i < MAXN; i++) par[i] = i;
}
void addEdge(int u, int v, int w){
edge[ecnt].v = v;
edge[ecnt].w = w;
edge[ecnt].nxt = head[u];
head[u] = ecnt++;
}
int getId(char s[]){
if(ma.find(s) == ma.end()) ma[s] = ++pcnt;
else return ma[s];
return pcnt;
}
int Find(int x){
if(par[x] != x) par[x] = Find(par[x]);
return par[x];
}
bool Union(int x, int y){
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return false;
par[fy] = fx;
return true;
}
void dfs(int s, int u, int fa){
for(int i = head[u]; i + 1; i = edge[i].nxt){
int v = edge[i].v;
if(v != s && v != fa && use[u][v]){
if(dp[v].w == -1){
if(dp[u].w > edge[i].w) dp[v] = dp[u];
else{
dp[v].w = edge[i].w;
dp[v].u = u;
dp[v].v = v;
}
}
dfs(s, v, u);
}
}
}
int kruskal(int s){
int ans = 0;
for(int i = 0; i < n; i++){
if(node[i].u == s || node[i].v == s) continue;
if(!Union(node[i].u, node[i].v)) continue;
use[node[i].u][node[i].v] = use[node[i].v][node[i].u] = true;
ans += node[i].w;
}
return ans;
}
int solve(int s){
sort(node, node + n);
int ans = kruskal(s);
for(int i = head[s]; i + 1; i = edge[i].nxt){
int v = edge[i].v;
int belong = Find(v);
if(Min[belong] > edge[i].w){
tmp[belong] = edge[i].v;
Min[belong] = edge[i].w;
}
}
int degree = 0;
for(int i = 1; i <= pcnt; i++){
if(Min[i] != MAX_INT){
degree++;
use[s][tmp[i]] = use[tmp[i]][s] = true;
ans += Min[i];
}
}
for(int i = degree + 1; i <= k; i++){
dp[s].w = -MAX_INT;
for(int j = 2; j <= pcnt; j++)
if(use[s][j]) dp[j].w = -MAX_INT;
dfs(s, 1, -1);
int temp, mi = MAX_INT;
for(int j = head[s]; j + 1; j = edge[j].nxt){
if(mi > edge[j].w - dp[edge[j].v].w){
mi = edge[j].w - dp[edge[j].v].w;
temp = edge[j].v;
}
}
if(mi >= 0) break;
use[s][temp] = use[temp][s] = true;
int u = dp[temp].u;
int v = dp[temp].v;
use[u][v] = use[v][u] = false;
ans += mi;
}
return ans;
}
int main(){
while(scanf("%d", &n) != EOF){
init();
int u, v, w;
char s1[50];
char s2[50];
ma["Park"] = ++pcnt;
for(int i = 0; i < n; i++){
scanf("%s%s%d", s1, s2, &w);
u = getId(s1), v = getId(s2);
addEdge(u, v, w);
addEdge(v, u, w);
node[i].u = u, node[i].v = v, node[i].w = w;
}
scanf("%d", &k);
int ans = solve(1);
printf("Total miles driven: %d\n", ans);
}
return 0;
}