# 题意
国王有两个数,每个大臣有两个数,国王始终站在队伍的最前面,每个大臣获得的奖励是他前面所有人左手上的乘积除以他右手上的数,请安排大臣的顺序,使得奖励最大的大臣获得的奖励最小
# 题解
直接将所有大臣按左右手上的数的乘积从小到大排序,得到的序列就是最优排队方案。
证明:
第 i 个大臣左手上的数是 Ai,右手上的数是 Bi。
若排队方案不是按 Ai∗Bi 从小到大排序的,则一定存在某两个相邻的人,满足 Ai∗Bi>Ai+1∗Bi+1
我们现在将这两个人的位置互换,然后考虑他们在交换前和交换后所获得的奖励是多少:
比较这四个数的大小关系, Ai,Bi 均大于0,所以可以将每个数除以 ∏i−1j=0Aj,然后乘 Bi∗Bi+1,得到:
由于 Ai>0,所以 Bi≤Ai∗Bi,并且 Ai∗Bi>Ai+1∗Bi+1,所以 max(Bi,Ai+1∗Bi+1)≤Ai∗Bi≤max(Bi+1,Ai∗Bi), 所以交换后两个数的最大值不大于交换前两个数的最大值。
而且交换相邻两个数不会对其他人的奖金产生影响,所以如果存在逆序,则将其交换,得到的结果一定不会比原来更差。
所以从小到大排好序的序列就是最优解,证毕。
时间复杂度
排序的时间复杂度是 O(nlogn)。
这道题目的时间复杂度瓶颈在高精度计算上,最坏情况下所有 Ai=9999Ai=9999,则前 i 个数的乘积大约是 4i 位,每次乘一个新的数就需要 4i 的计算量,总共的计算量是 O(n2)。
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define ld long double 4 #define fi first 5 #define se second 6 #define pii pair<int,int> 7 using namespace std; 8 const int N=1010; 9 pii p[N]; 10 int n; 11 vector<int> mul(vector<int> &A, int b) 12 { 13 vector<int> C; 14 int t = 0; 15 for (int i = 0; i < A.size() || t; i ++ ) 16 { 17 if (i < A.size()) t += A[i] * b; 18 C.push_back(t % 10); 19 t /= 10; 20 } 21 22 return C; 23 } 24 vector<int> div(vector<int> &A, int b) 25 { 26 vector<int> C; 27 int r = 0; 28 for (int i = A.size() - 1; i >= 0; i -- ) 29 { 30 r = r * 10 + A[i]; 31 C.push_back(r / b); 32 r %= b; 33 } 34 reverse(C.begin(), C.end()); 35 while (C.size() > 1 && C.back() == 0) C.pop_back(); 36 return C; 37 } 38 vector<int> max_(vector<int>a,vector<int>b){ 39 if(a.size()>b.size()) return a; 40 if(a.size()<b.size()) return b; 41 if(vector<int>(a.rbegin(),a.rend()) > vector<int>(b.rbegin(),b.rend())) 42 return a; 43 return b; 44 } 45 int main() { 46 ios::sync_with_stdio(0); 47 cin.tie(0); 48 cout.tie(); 49 cin>>n; 50 for(int i=0;i<=n;i++) { 51 int x,y; 52 cin>>x>>y; 53 p[i]={x * y,x}; 54 } 55 sort(p+1,p+n+1); 56 vector<int>res(1,0); 57 vector<int>pos(1,1); 58 for(int i=0;i<=n;i++) { 59 if(i) res=max_(res,div(pos,p[i].fi/p[i].se)); 60 pos=mul(pos,p[i].se); 61 } 62 for(int i=res.size()-1;i>=0;i--) 63 cout<<res[i]; 64 cout<<endl; 65 }