题目传送门
Part1. 思路
这一题要找到交流次数最多的一组答案,所以理所当然地想到要尽可能的多用交流次数。
所以要尽可能的多匹配还有交流次数的人去交流。
但是如果随便匹配,就会出现这样的情况:
1
3
1 2 3
这组数据的答案很明显:
3
1 3
2 3
2 3
假如随便匹配,就可能得到这样的答案:
2
1 2
2 3
出现这种情况的原因是什么呢?
很容易发现,随便匹配得出的过程中,在前两人已经用完交流次数时,交流次数最多的第 \(3\) 个人的交流次数还有 \(2\),很明显没有物尽其用,出现了浪费。
所以需要尽量规避这种情况。
然后就会想到让交流次数最多的两人进行交流,来获得最多的交流次数。
这时候就又会有两种不同的想法:
-
将交流次数最多的两人中交流次数较少的那个人的交流次数用完,再找新的交流次数最多的两人。
-
将交流次数最多的两人的交流次数各使用 \(1\),然后找新的交流次数最多的两人。
如果使用想法一,在下面的数据中就会出错:
1
5
5 5 5 5 5
正确答案为:
12
4 5
2 3
1 5
3 4
1 2
4 5
2 3
1 5
3 4
1 2
4 5
2 3
但是用想法一得出的答案是:
10
4 5
4 5
4 5
4 5
4 5
2 3
2 3
2 3
2 3
2 3
很明显,有一个人的交流次数完全没有使用,完全被浪费。
所以也需要避免出现这种情况。
现在我们便得到了正解:
维护一个大根堆,将所有的有交流次数的人压入队列,当队列中还至少有两个人时,将队首的两人弹出,将他们的交流次数 \(-1\),如果还有剩余的交流次数,就再将它们压回队列。
Part2. 代码
#include <bits/stdc++.h>
using namespace std;int t,n,ans,a[200010];
pair<int,int> tot[200010];int main () {cin>> t;while (t--) {cin>> n;priority_queue<pair<int,int> > q;for (int i=1;i<=n;i++) {cin>> a[i];if (a[i]>0) q.push ({a[i],i});}ans=0;while (q.size ()>=2) {pair<int,int> u=q.top ();q.pop ();pair<int,int> v=q.top ();q.pop ();tot[++ans]={min (u.second,v.second),max (u.second,v.second)};u.first--;v.first--;if (u.first) q.push (u);if (v.first) q.push (v);}cout<< ans<< "\n";for (int i=1;i<=ans;i++)cout<< tot[i].first<< " "<< tot[i].second<< "\n";}return 0;
}
AC记录