题目链接:https://ac.nowcoder.com/acm/contest/1056/B
书P368
①:Kruskal算法
https://blog.nowcoder.net/n/012b92baab1f4ee59bb17b9bcb8dadb4
本题中用这个算法求去掉根节点1后的最小生成树
②贪心策略
本题要找满足1号节点度数不超过S的最小生成树
如果去掉节点1后,连通块个数为T大于S,则无解。接下来我们从每个连通块中寻找与节点1边长最短的点,加入生成树
然后还可以改S-T条边,每条(1,x)的边,我们找到与1的本身路径最长的一条减去(1,x)的长度,那么整个生成树长度就减少了
/*#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100;
struct rec
{
int x,y,z;
}edge2[maxn];//edge2为去掉根节点1后的最小生成树
int edge3[maxn][maxn],fa[maxn],edge1[maxn],t[maxn],d[maxn];//fa为并查集,edge1为1到各个点的边长度,t为判断当前边是否已经加入,d为scan时避免重复搜点
int n,m,ans=0,to=0,minn,dian,s;//minn是scan时最小边长度,dian是与1距离最近的点,s是1的最大度数
bool operator <(rec f1,rec f2)
{
return f1.z<f2.z;
}
int get(int x)//搜索所在集合
{
if(x==fa[x])
{
return x;
}
return fa[x]=get(fa[x]);
}
int scan(int x)//搜索每个集合的点与1的最短边
{
if(edge1[x]<minn)
{
minn=edge1[x];
dian=x;
}
if(x==fa[x])
{
return x;
}
return fa[x]=get(fa[x]);
}
int dfs(int u,int maxb)
{
for(int i=1;i<=n;i++)
{
if(edge3[u][i]!=-1&&d[i]==0)
{
dfs(i,max(maxb,edge3[u][i]));
}
}
}
void init()
{
memset(edge1,INF,sizeof(edge1));
memset(edge3,-1,sizeof(edge3));
}
int main()
{
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
if(x!=1&&y!=1)
{
edge2[i].x=x;
edge2[i].y=y;
edge2[i].z=z;
to++;
}
else
{
edge1[max(x,y)]=z;
}
edge3[i].x=x;edge3[i].y=y;edge3[i].z=z;
}
cin>>s;
sort(edge2+1,edge2+1+to);//按权值从小到大排序
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
int shu=n-1;
for(int i=1;i<=to;i++)
{
int x=get(edge2[i].x);
int y=get(edge2[i].y);
if(x==y)
{
continue;
}
edge3[edge2[i].x][edge2[i].y]=z; edge3[edge2[i].y][edge2[i].x]=z;
fa[x]=y;//合并集合
shu--;
}
if(shu>s)
{
cout<<"can not be a tree!!!"<<endl;
return 0;
}
for(int i=2;i<=n;i++)
{
if(d[i]==0)
{
minn=INF;
scan(i);
edge3[1][dian]=minn; edge3[dian][1]=minn;
t[dian]=1;
}
}
memset(d,0,sizeof(d));
d[1]=1;
dfs(1,-1);
cout<<ans<<endl;
return 0;
}*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#define INF 0x3f3f3f3f
#define MAXN 22
using namespace std;
struct Edge{
int u, v, d;
Edge() {}
Edge(int a, int b, int c) : u(a), v(b), d(c) {}
bool operator < (const Edge &e) const {
return d < e.d;
}
};
int n, m, k;
int cnt;
int ans;
int parent[MAXN]; // 并查集
map<string, int> nodes;
vector<Edge> edges;
Edge dp[MAXN];
int g[MAXN][MAXN];
bool tree[MAXN][MAXN]; // tree[i][j]=true表示<i, j>这条边在最小生成树中
int minEdge[MAXN];
int find(int p) {
if (p == parent[p]) return parent[p];
return parent[p] = find(parent[p]);
}
void un(int p, int q) {
parent[find(p)] = find(q);
}
void kruskal() {
sort(edges.begin(), edges.end());
for (int i = 0; i < edges.size(); i++) {
int p = edges[i].u;
int q = edges[i].v;
if (p == 1 || q == 1) continue; // 忽略根节点
if (find(p) != find(q)) {
un(p, q);
tree[p][q] = tree[q][p] = 1;
ans += edges[i].d;
}
}
}
void dfs(int cur, int pre) {
for (int i = 2; i <= cnt; i++) {
if (i == pre || !tree[cur][i]) continue;
if (dp[i].d == -1) {
if (dp[cur].d > g[cur][i]) dp[i] = dp[cur];
else {
dp[i].u = cur;
dp[i].v = i;
dp[i].d = g[cur][i];
}
}
dfs(i, cur);
}
}
void solve() {
int keyPoint[MAXN];
for (int i = 2; i <= cnt; i++) {
if (g[1][i] != INF) {
// 点i在哪颗最小生成树中
int color = find(i);
// 每颗最小生成树中距离根节点最近的点与根节点的距离
if (minEdge[color] > g[1][i]) {
minEdge[color] = g[1][i];
keyPoint[color] = i;
}
}
}
for (int i = 1; i <= cnt; i++) {
if (minEdge[i] != INF) {
m++;
tree[1][keyPoint[i]] = tree[keyPoint[i]][1] = 1;
ans += g[1][keyPoint[i]];
}
}
// 由i-1度生成树得i度生成树
for (int i = m + 1; i <= k; i++) {
memset(dp, -1, sizeof(dp));
dp[1].d = -INF;
for (int j = 2; j <= cnt; j++)
if (tree[1][j]) dp[j].d = -INF;
dfs(1, -1); // dp预处理
int idx, minnum = INF;
for (int j = 2; j <= cnt; j++) {
if (minnum > g[1][j] - dp[j].d) {
minnum = g[1][j] - dp[j].d;
idx = j;
}
}
if (minnum >= 0) break;
tree[1][idx] = tree[idx][1] = 1;
tree[dp[idx].u][dp[idx].v] = tree[dp[idx].v][dp[idx].u] = 0;
ans += minnum;
}
}
void init() {
memset(g, 0x3f, sizeof(g));
memset(tree, 0, sizeof(tree));
memset(minEdge, 0x3f, sizeof(minEdge));
m = 0;
cnt = 1;
ans = 0;
nodes["Park"] = 1;
for (int i = 0; i < MAXN; i++)
parent[i] = i;
}
int main() {
scanf("%d", &n);
string s1, s2;
int d;
init();
for (int i = 1; i <= n; i++) {
cin >> s1 >> s2 >> d;
if (!nodes[s1]) nodes[s1] = ++cnt;
if (!nodes[s2]) nodes[s2] = ++cnt;
int u = nodes[s1], v = nodes[s2];
edges.push_back(Edge(u, v, d));
g[u][v] = g[v][u] = min(g[u][v], d);
}
scanf("%d", &k);
kruskal(); // 忽略根节点先计算一次最小生成树,此时得到一个森林
solve();
printf("Total miles driven: %d\n", ans);
return 0;
}来源:https://blog.csdn.net/u011265346/article/details/42919889

京公网安备 11010502036488号