目前为止见过的最复杂的题了(对代码实现能力要求很高)。。。。。码了好久才想通。
接下来来剖析步骤--
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);}