最小k度生成树
题意:以Park为1号点,求1号点度数不超过s的最小生成树是多少,点数不超过30。最小k度生成树的求法:
假设限制度数的点为v0,除去v0后,剩下的点会构成m个连通块,如果限制的度数小于m,显然是无解的。
对除去v0后的所有连通块求最小生成树,在这m个最小生成树中,分别选择一条与v0相连且权值最小的边加入到生成树中,构成的生成树就是最小m度生成树。
如果要求m+1度的生成树,只需要选取一条最小且在生成树以外的边,加入到生成树中,此时的生成树会产生一个环,除去环中权值最大的边,就是最小m+1度生成树,最小m+2度生成树也是同理(在m+1度的基础上求)
把v0设置为根节点,环中最大的边,可以通过dp记录根节点到当前节点中权值最大的边是多少,取环中最大权值就能O(1)取,O(V)预处理。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
const int MaxnN = 200+10;
const int MaxnM = 2e5+10;
const int INF = 0x3f3f3f3f;
const ll LINF = 1e18;
struct Edge {
int u, v, w, next;
bool used; // 是否为生成树的边
} edge[MaxnM];
struct Node {
int v, w, i;
Node(int _v, int _w, int _i):v(_v), w(_w), i(_i){}
bool operator < (const Node &a1) const {
return w > a1.w;
}
};
// dp 记录根节点到当前节点中的最大边是多少, pos为相应边的下标
int h[MaxnN], edge_cnt, dp[MaxnN], pos[MaxnN];
int m, s, v0, tot = 0;
map <string, int> mp;
bool vis[MaxnN];
priority_queue <Node> qu;
void add(int u, int v, int w) {
edge[edge_cnt].u = u; edge[edge_cnt].v = v;
edge[edge_cnt].w = w; edge[edge_cnt].used = false;
edge[edge_cnt].next = h[u];
h[u] = edge_cnt++;
}
// 边划分连通块边求连通块的最小生成树,并算出生成树中
// 离根节点v0的最近的边是多少
void prim(int u, int& ans) {
while(!qu.empty()) qu.pop();
int cost = INF, E;
qu.push(Node(u, 0, -1));
while(!qu.empty()) {
Node cur = qu.top(); qu.pop();
u = cur.v;
if(vis[u]) continue;
vis[u] = true; ans += cur.w;
if(cur.i != -1) edge[cur.i].used = edge[cur.i^1].used = true;
for(int i = h[u]; i != -1; i = edge[i].next) {
Edge &e = edge[i];
if(!vis[e.v] && e.v != v0) qu.push(Node(e.v, e.w, i));
else if(e.v == v0 && cost > e.w) { // 如果是与根节点的边就记录,取最小值
cost = e.w; E = i;
}
}
}
if(cost != INF) { // 连通块与v0最近的边直接加入到生成树中
ans += cost;
edge[E].used = edge[E^1].used = true;
}
}
void dfs(int u) {
for(int i = h[u]; i != -1; i = edge[i].next) {
Edge &e = edge[i];
if(e.used && dp[e.v] == -INF) {
dp[e.v] = dp[u]; pos[e.v] = pos[u];
if(dp[e.v] < e.w) {
dp[e.v] = e.w; pos[e.v] = i;
}
dfs(e.v);
}
}
}
int main(void)
{
int w, x, y;
char u[50], v[50];
scanf("%d", &m);
for(int i = 0; i < MaxnN; ++i) h[i] = -1;
edge_cnt = 0;
for(int i = 0; i < m; ++i) {
scanf("%s%s%d", u, v, &w);
if(mp.find(u) == mp.end()) mp[u] = tot++;
if(mp.find(v) == mp.end()) mp[v] = tot++;
x = mp[u]; y = mp[v];
add(x, y, w); add(y, x, w);
}
scanf("%d", &s);
memset(vis, false, sizeof(vis));
v0 = mp["Park"];
int cnt = 0, ans = 0;
for(int i = 0; i < tot; ++i) {
if(!vis[i] && i != v0) {
prim(i, ans); cnt++;
}
}
int tmp = ans;
for(int i = cnt+1; i <= s; ++i) {
// 生成树有删边和加边的情况,所以每次dp都需要重新算
for(int j = 0; j < tot; ++j) dp[j] = -INF;
dp[v0] = -1; dfs(v0);
int cost = INF, E, v1;
for(int j = h[v0]; j != -1; j = edge[j].next) {
Edge &e = edge[j];
if(!e.used && cost > e.w-dp[e.v]) {
cost = e.w-dp[e.v]; E = j; v1 = e.v;
}
}
if(cost != INF) {
tmp += cost;
edge[E].used = edge[E^1].used = true;
edge[pos[v1]].used = edge[pos[v1]^1].used = false;
ans = min(ans, tmp);
}
}
printf("Total miles driven: %d\n", ans);
return 0;
}
浙公网安备 33010602011771号