目前为止见过的最复杂的题了(对代码实现能力要求很高)。。。。。码了好久才想通。
接下来来剖析步骤--
1.先不考虑1号节点,跑一遍克鲁斯卡尔,这样得到的就是各个联通块内部的最小生成树(也就是森林)。
2,。贪心的想,将每个联通块内到根节点最短的端点找出并连接。
3. 连接之后我们假设用了t条边,而题目要求使用s条边,那么我们就还有(s-t)条边可以使用,因此我们可以考虑用与1直接相连的边的权值去删掉(1,x)中边权最大的边,(这样既可保持联通性,又减少了权值)。
4.关键在于如何实现找到路径上的最值。
我们看这段代码,乍看会tle,实则不然,我们用tree函数储存原本就在生成树内的边(也就是我们要讨论的点) ,这个时候由于生成树的特性,遍历到叶子节点的时候就会停止遍历了。(可以画张图看一下。)(可以理解成把一个树从上到下更新了一遍。)
好了,分析完毕,以后必不会再写了,,当成板子收起来。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct rec
{
long long x,y,z;
} edge[5000],dp[5001];
int n,m,ans;
int fa[10000];
int tot;
map<string ,int > name;
vector<int> block[1000];
int tree[1000][1000];
int mind[1000];
long long g[1000][1000];
bool vis[101];
int all;
bool operator <(rec a,rec b)
{
return a.z < b.z;
}
int get(int x)
{
if(x == fa[x]) return x;
return fa[x] = get(fa[x]);
}
void kruskal() //生成的是一颗森林
{
for(int i = 1 ; i <= n ; i++)
{
int x = get(edge[i].x), y =get(edge[i].y);
if(x == y || edge[i].x == 1 || edge[i].y == 1)continue ;
fa[x] = y;
tree[edge[i].x][edge[i].y] = tree[edge[i].y][edge[i].x] = 1;
ans += edge[i].z;
}
}
//穷尽所有可能,去更新能到达该点的最值
void dfs(int cur, int pre) {
// cout << cur << " " << pre << endl;
for (int i = 2; i <= tot; i++) {
// cout << i << endl;
if (i == pre || !tree[cur][i]){ continue;}
if (dp[cur].z > g[cur][i]) dp[i] = dp[cur];
else {
dp[i].x = cur;
dp[i].y = i;
dp[i].z = g[cur][i];
}
dfs(i, cur);
}
}
void solve()
{
int point[101];
for(int i = 2; i <= tot ; i++)
{
if(g[1][i] !=INF)
{
if(g[1][i] < mind[get(i)])
{
mind[get(i)] = g[1][i];
point[get(i)] = i;
}
}
}
//各连通块与1连边。
int cnt = 0;//联通块数量
for(int i = 2 ; i <= tot ; i++)
{
if(!vis[get(i)])
{
vis[get(i)] = 1;
ans += mind[get(i)];
tree[1][point[get(i)]] = tree[point[get(i)]][1] = 1;
cnt++;
}
}
// cout <<ans << endl;
for(int i = cnt + 1 ; i <= all ; i++)
{
memset(dp,-1,sizeof dp);
// maxx w - z;
dp[1].z = -INF;
for(int j = 2 ; j <= tot ; j++) if(tree[1][j]) dp[j].z = -INF;
dfs(1,0);
int idx , minx = INF;
for(int j = 2 ; j <= tot ; j++)
{
if(minx > g[1][j] - dp[j].z)
{
minx = g[1][j] - dp[j].z;
idx = j;
}
}
if(minx >= 0) break;
tree[1][idx] = tree[idx][1] = 1;
tree[dp[idx].x][dp[idx].y] = tree[dp[idx].y][dp[idx].x] = 0;
ans += minx;
}
}
int main()
{
memset(tree,0,sizeof tree);
memset(g,0x3f,sizeof g);
memset(edge,0x3f,sizeof edge);
memset(mind,0x3f,sizeof mind);
cin >> n;
name["Park"] = ++tot;
//for(int i = 1; i <= n ; i++)
for(int i = 1; i <= n ; i++)
{
string s1,s2;
long long d;
cin >> s1 >> s2 >> d;
if(!name[s1]) name[s1] = ++tot;
if(!name[s2]) name[s2] = ++tot;
g[name[s1]][name[s2]] = g[name[s2]][name[s1]] = min(d,g[name[s1]][name[s2]]);
edge[i].x = name[s1],edge[i].y = name[s2],edge[i].z = d;
}
sort(edge + 1 , edge + 1 + n); //边
cin >> all;
for(int i = 1 ; i <= tot ; i++) fa[i] = i;
kruskal();
solve();
printf("Total miles driven: %d\n", ans);
}