链接:
https://www.nowcoder.com/acm/contest/52/K
题意:
给定n个点,每个点有自己的权值, 然后让你添加n-1条边,使其边权和最大, 边权的定义是两点的点权和除2。
分析:
一开始我想到的是裸的最小生成树, 用优先队列优化的prim算法200ms过了,复杂度是O(mlogn),m是边数,n是点数 ,代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct edge{int to, dis; edge(int _to, int _dis):to(_to), dis(_dis){}}; 4 const int maxn = 1e3 + 7; 5 const int maxm = maxn * maxn; 6 vector<edge> G[maxn]; 7 int a[maxn]; 8 int n; 9 int prim(){ 10 int ans = 0; 11 int dis[maxn]; 12 bool vis[maxn]; 13 priority_queue<pair<int, int>, vector<pair<int, int> >, less<pair<int, int> > > q; 14 memset(dis, -1, sizeof(dis)); 15 memset(vis, 0, sizeof(vis)); 16 for(int i = 0; i < G[0].size(); i++){ 17 int v = G[0][i].to, d = G[0][i].dis; 18 dis[v] = d; 19 q.push(make_pair(d,v)); 20 } 21 dis[0] = 0, vis[0] = 1; 22 while(!q.empty()){ 23 int u = q.top().second, d = q.top().first; 24 q.pop(); 25 if(vis[u]) continue; 26 vis[u] = 1; 27 ans += d; 28 for(int i = 0; i < G[u].size(); i++){ 29 int v = G[u][i].to, d = G[u][i].dis; 30 if(!vis[v] && (dis[v] < d)){//注意prim的松弛条件别写错 31 dis[v] = d; 32 q.push(make_pair(dis[v], v)); 33 } 34 } 35 } 36 37 return ans; 38 } 39 int main(){ 40 int T; 41 scanf("%d", &T); 42 while(T--){ 43 for(int i = 0; i < maxn; i++) G[i].clear(); 44 scanf("%d", &n); 45 for(int i = 0; i < n; i++) scanf("%d", &a[i]); 46 for(int i = 0; i < n; i++) 47 for(int j = i + 1; j < n; j++){ 48 G[i].push_back(edge(j, (a[i] + a[j]) / 2)); 49 G[j].push_back(edge(i, (a[i] + a[j]) / 2)); 50 } 51 cout << prim() << " "; 52 } 53 return 0; 54 }
但是看了别人的时间后发现都是10ms以内的,以上的算法大约在n去到10^3已经极限了, 因为完全图边数是n*(n-1)/2
想了想其实这题有个简单的结论,就是“除权值最大的点外,每个点都与权值最大的点相连”,这样可以保证边权取到最大, 而且刚好n-1条边。
1 #include <bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 int a[1003]; 5 int n, T; 6 scanf("%d", &T); 7 while(T--){ 8 scanf("%d", &n); 9 for(int i = 0; i < n; i++) scanf("%d", &a[i]); 10 sort(a, a+n); 11 int ans = 0; 12 for(int i = 0; i < n - 1; i++) ans += (a[n-1] + a[i]) / 2; 13 printf("%d ", ans); 14 } 15 return 0; 16 }