2022-03-30每日刷题打卡
代码源——每日一题
新国王游戏 - 题目 - Daimayuan Online Judge
又到了 H 国国庆, 国王再次邀请 n 位大臣来玩有奖游戏。上次国庆被众臣吐槽国王小气后,国王决定今年大方点,改变游戏规则且不再参与游戏,免得被大臣们质疑。首先, 他让每位大臣在左、 右手上面分别写下一个正整数。然后让这 n 位大臣排成一排。排好队后, 所有的大臣都会获得国王奖赏的若千金币, 每位大臣获得的金币数分别是:排在该大臣后面的所有人的左手上的数的乘积乘以他自己右手上的数。国王希望所有大臣获得的金币数之和最多,所以他想请你帮他重新安排一下队伍的顺序。
简而言之,给定 n 对数 ai,bi,找到一种排列顺序,使得 b1×a2∗a3∗…an+b2×a3∗a4∗…an+⋯+bn最大,求最大值,由于答案可能很大,需要对 1000000007取模
输入格式:
第一行,包含一个整数 n。 第二行到第 n+1 行,包含两个整数 ai,bi
输出格式:
输出一行,表示按某种排序后的 b1×a2∗a3∗…an+b2×a3∗a4∗…an+⋯+bn 的最大值对 1000000007 取模的结果
样例输入
2
1 2
3 4
样例输出
10
说明
只有两种情况:
1 2
3 4
(2∗3)+4=10
3 4
1 2
(4∗1)+2=6
所以答案为 10
数据限制
对于 100% 的数据,保证 1≤n≤106,1≤ai,bi≤230
这题的解法我觉得比较魔幻,倒不是难,只是不知道该怎么和大家说。
主要就是自定义排序规则。我们可以把左手右手当成数对的两个元素,存入数组里然后自定义一个特殊的排序规则,从而使得排序后的结果最大。也就说重点就在这了,我们该怎么自定义排序。
首先我们知道,计算金币的方法是后面所有人的左手金币乘积乘上当前自己右手上金币。当我们像样例一样,只有两个人时,我们就要看,谁在前面可以使得我们获得金币最多,哪个方法多肯定是要放在前面。这其实就和冒泡排序差不多。那么我们的cmp函数就是判断a在b前面的金币数(b.second + a.second * b.first)和b在a前面的金币数(a.second + b.second * a.first)谁大,哪个大我们就选哪个方法,然后数组排完序后计算金币数即可。
(在这里我是升序排序的,即金币少的排前面,这样我计算金币数是就可以从数组开头开始算起,如果是降序的样子就要从末尾开始算起)
(这题有个非常类似的最小字符串像看看自己掌握程度可以去试试)
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int MOD = 1000000007;
bool cmp(PII a, PII b)
{
return b.second + a.second * b.first < a.second + b.second * a.first;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
vector<PII>v(n);
for (int i = 0; i < n; i++)cin >> v[i].first >> v[i].second;
sort(v.begin(), v.end(), cmp);
ll sum = 1,res=v[0].second;
for (int i = 0; i < n; i++)
{
if(i>0)res = (res + (sum * v[i].second)%MOD)%MOD;
sum = (sum * v[i].first)%MOD;
}
cout << res%MOD << endl;
return 0;
}