题目笔记——P8957 「CGOI-3」巫泡弹弹乐
题目大意
给定 \(n\) 个点,每个点有两个值:\(a_i\) 和 \(b_i\)。\(i\)、\(j\)两点之间有一条无向边边权为 \(\max(a_i,a_j)+\max(b_i,b_j)\),求这 \(n\) 个点组成的最小生成树。
解题思路
方法一:
将 \(n\) 个点以 \(a\) 从小到大进行排序后依次放入生成树,每次放入点与原本生成树连边的 \(a\) 绝对是当前这个点的 \(a\),计算出答案为 \(\sum\limits_{i=2}^na_i\),可以证明这为最优答案。
每次将放入点与原生成树中的最小值连边保证最优。
每次放入点有两种情况:
- 放入点的 \(b\) 大于等于原生成树中的最小 \(b\):记一次放入点的 \(b\) 的答案,并再也不重复使用。
- 放入点的 \(b\) 小于原生成树中的最小 \(b\):记一次原生成树中的最小 \(b\),并用放入点的 \(b\) 替换成原生成树中的最小 \(b\)。
由此,可以证明除序列 \(b\) 中的最小值以外,所有的都只会记一次答案,答案计为:\(\sum\limits_{i=2}^nb_i\),为最优答案。
因为 \(a\) 和 \(b\) 都为中最优答案,所以此方案为最优解(之一)。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 20;
struct node{
int a, b, i;
}a[N];
int n, sum, mna = 1e18, mnb = 1e18;
bool cmp(node x, node y){return x.a < y.a;}
signed main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i].a;
a[i].i = i;
sum += a[i].a;
mna = min(mna, a[i].a);
}
for(int i = 1; i <= n; i++){
cin >> a[i].b;
sum += a[i].b;
mnb = min(mnb, a[i].b);
}
cout << sum - mna - mnb << endl;
sort(a + 1, a + n + 1, cmp);
for(int i = 2, mn = a[1].b, mni = 1; i <= n; i++){
printf("%lld %lld\n", a[mni].i, a[i].i);
if(a[i].b <= mn){
mn = a[i].b;
mni = i;
}
}
return 0;
}
方法二:
依旧将 \(n\) 个点以 \(a\) 从小到大进行排序,此时对于任意 \(i\)、\(j\) 满足 \(j<i\),要么 \(b_j<b_i\),最优,要么 \(b_j>b_i\),那就也依次放入生成树,\(i\) 为当前要加入的节点,\(j\) 为当前生成树中 \(b\) 最小的下标,可以直接连接 \(i\)、\(j\)。
- 当 \(b_j<b_i\) 时,此时 \(i\) 可以完全将 \(j\) 覆盖,答案增加 \(a_i+b_i\)。
- 当 \(b_j>b_i\),用 \(i\) 替换掉 \(j\),答案增加 \(a_i+b_j\)。
最后输出答案和边就行了。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 20;
struct node{
int a, b, i;
}a[N];
int n, ans;
vector<pair<int, int> > vec;
bool cmp(node x, node y){return x.a < y.a;}
signed main(){
cin >> n;
for(int i = 1; i <= n; i++) scanf("%lld", &a[i].a);
for(int i = 1; i <= n; i++) scanf("%lld", &a[i].b);
for(int i = 1; i <= n; i++) a[i].i = i;
sort(a + 1, a + n + 1, cmp);
for(int i = 2, mn = 1; i <= n; i++){
vec.push_back({a[i].i, a[mn].i});
if(a[i].b > a[mn].b) ans += a[i].a + a[i].b;
else{
ans += a[mn].b + a[i].a;
mn = i;
}
}
cout << ans << endl;
for(pair<int, int> x : vec){
printf("%lld %lld\n", x.first, x.second);
}
return 0;
}

浙公网安备 33010602011771号