果然,一开始我又往DP方面想。还好,我及时发现了这个根本没法描述状态(就算用集合,也只是阶乘级到指数级,况且n<=1000,根本不可能),于是开始往贪心的方面想。

我们设左手上的数为A[i],右手上的数为B[i]

解1:
考虑到获得最多金币的大臣应该在最后一个,或者说,靠近最后一个。因为最后一个大臣计算金币时,分子是最大的,为其它大臣A的乘积。为什么说是靠近呢?因为有可能最后一个大臣的B[i]比倒数第二个大臣的B[i]大,导致获得的金币反而少。那我们先让最后一个大臣获得的金币尽量少试试。
第一次计算,可以计算其他人A[i]的积除以某个人的B[i],把最小的放在队列的最后,以后的计算把除了自己和计算过了的人的A[i]相乘,再除以这个人的B[i],同样把最小的放在后面。直到所有人都放完了就完了。
下面让我们简单证明下,为什么要把尽量小的放在后面:

设放在最后的大臣的左手为L1L_1,右手为R1R_1,他的根据之前描述的算法算出来的金币最少。倒数第二个大臣的左手为L2L_2,右手为R2R_2,这个大臣是随机的,但是把他放在倒数第一的位置时获得的金币比最后的大臣多。再设倒数第三个到国王的左手的乘积为MM,则有:

COIN1=M∗L2R1

COIN_1=\frac{M*L_2}{R_1}

COIN2=MR2

COIN_2=\frac{M}{R_2} 交换后有

COIN1=MR1

COIN_1=\frac{M}{R_1}

COIN2=M∗L1R2

COIN_2=\frac{M*L_1}{R_2}
交换后的COIN2COIN_2比COIN1COIN_1大,说明不会有更优解。

那如果COIN1=COIN2COIN_1=COIN_2呢? 可以用一个贪心策略:尽量让LL大的数放在后面,因为这样之前的LL的乘积会尽量小。
若此时COINCOIN和LL均相同,那么RR是相同的,随便选就可以了。最后的答案就是计算过程中出现的最大COINCOIN。可以再一次使用贪心。若在计算过程中COINCOIN的值非严格递减,则之后的COINCOIN也会递减,这时便已得到了答案,不用算完。

以上便是我做题时想到的思路。很幸运,居然得到了90分。其实以上思路并不严密。我们每次选择的都是放在当前位置COINCOIN最小的大臣,实际却不能保证先选的大臣的COINCOIN在最后是最大值。同时,我们也不能保证COINCOIN和LL相同的大臣RR也相同。一个典型的例子是:

1 1 //国王
1 1
1 110
1 119
1 120

我们肯定会先选2,3,4号大臣,但是我们在计算时算到倒数第二个大臣就退出了。

一个解决方法是:只当金币严格递减时才退出。这样就能保证答案正确了。但是这样肯定会使计算速度放慢。幸运的是,最后一个点用这种方法在时间上刚好能过。

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
using std::cin;
using std::cout;
using std::endl;
#define FOR(x, f, t) for(int x = (f); x <= (t); x++)
inline int readIn()
{int a;scanf("%d",&a);return a;
}const int maxn = 1005;
int n;
int kingA, kingB;
int A[maxn],B[maxn];
bool vis[maxn];
struct BigInt
{static const int digit = 1e9;static const int size = 445;std::vector<long long> num;int length;BigInt(int init = -1){if(init==-1){num.resize(size,digit-1);length = size;}else{num.resize(size);num[0] = init;length = bool(init);}}void operator*= (int b){for(int i=0; i<length; i++){num[i]*=b;}for(int i=0; i<length; i++){num[i+1]+=num[i]/digit;num[i]%=digit;}if(num[length]) length++;}void operator/= (int b){long long div = 0;long long mod = 0;for(int i=length-1; i>=0; i--){num[i]+=mod * digit;mod = num[i] % b;num[i] = num[i] / b;}if(length && !num[length - 1]) length--;}BigInt operator/ (int b){BigInt ret = *this;ret/=b;return ret;}bool operator< (const BigInt& b) const{if(length<b.length) return true;else if(length>b.length) return false;for(int i=length-1; i>=0; i--){if(num[i]<b.num[i]) return true;else if(num[i]>b.num[i]) return false;}return false;}bool operator== (const BigInt& b) const{return !(*this<b) && !(b<*this);}bool operator>= (const BigInt& b) const{return b<*this || *this==b;}void print(){if(length) printf("%lld",num[length-1]);else printf("0");for(int i = length-2; i>=0; i--){printf("%09lld",num[i]);}puts("");}
};void run()
{n=readIn();kingA=readIn();kingB=readIn();FOR(i, 1, n){A[i]=readIn();B[i]=readIn();}BigInt mul = kingA; //注意高精度FOR(i, 1, n){mul*=A[i];}BigInt ans = 0;FOR(i, 1, n){BigInt minVal;int minIndex = 0;FOR(j, 1, n) //找到放在倒数第i个位置获得金币最少的大臣{if(vis[j]) continue;BigInt temp = mul/A[j]/B[j];if(temp<minVal || temp==minVal && A[j]>A[minIndex]){minVal = temp;minIndex = j;}}mul/=A[minIndex];if(minVal>=ans) //这里只能是大于等于{ans=minVal;}elsebreak;vis[minIndex] = true;}ans.print();
}int main()
{run();return 0;
}

解2

设相邻两个人分别为ii和i+1i+1。左手数字为aia_i和ai+1a_{i+1},右手数字为bib_i和bi+1b_{i+1}。两人的金币数为wiw_i和wi+1w_{i+1}。
记Pi=a1∗a2∗a3∗...∗aiP_i=a_1*a_2*a_3*...*a_i
可得:

wi=Pi−1bi

w_i=\frac {P_{i-1}}{b_i}

wi+1=Pibi+1

w_{i+1}=\frac {P_i}{b_{i+1}}

Pi=Pi−1∗ai

P_i=P_{i-1}*a_i
那么

wi+1=Pi−1∗aibi+1=wi∗ai∗bibi+1

w_{i+1}=\frac {P_{i-1}*a_i}{b_{i+1}}=\frac {w_i*a_i*b_i}{b_{i+1}}
发现,若使用递推,对于相邻的二元组(i,i+1)(i,i+1)来说,i+1i+1只受ai∗bia_i*b_i影响,因此对aia_i,bib_i排序就可以了,相同的值不用加其它限制条件。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
using std::cin;
using std::cout;
using std::endl;
#define FOR(x, f, t) for(int x = (f); x <= (t); x++)
inline int readIn()
{int a;scanf("%d",&a);return a;
}const int maxn = 1005;
int n;
int kingA, kingB;
struct People
{int A;int B;void input(){A=readIn();B=readIn();}bool operator< (const People& b) const{return A*B < b.A*b.B;}
} people[maxn];
struct BigInt
{static const int digit = 1e9;static const int size = 445;std::vector<long long> num;int length;BigInt(int init = -1){if(init==-1){num.resize(size,digit-1);length = size;}else{num.resize(size);num[0] = init;length = bool(init);}}void operator*= (int b){for(int i=0; i<length; i++){num[i]*=b;}for(int i=0; i<length; i++){num[i+1]+=num[i]/digit;num[i]%=digit;}if(num[length]) length++;}void operator/= (int b){long long div = 0;long long mod = 0;for(int i=length-1; i>=0; i--){num[i]+=mod * digit;mod = num[i] % b;num[i] = num[i] / b;}if(length && !num[length - 1]) length--;}BigInt operator/ (int b){BigInt ret = *this;ret/=b;return ret;}bool operator< (const BigInt& b) const{if(length<b.length) return true;else if(length>b.length) return false;for(int i=length-1; i>=0; i--){if(num[i]<b.num[i]) return true;else if(num[i]>b.num[i]) return false;}return false;}void print(){if(length) printf("%lld",num[length-1]);else printf("0");for(int i = length-2; i>=0; i--){printf("%09lld",num[i]);}puts("");}
};void run()
{n=readIn();kingA=readIn();kingB=readIn();FOR(i, 1, n){people[i].input();}std::sort(people+1, people+1+n);BigInt ans = 0;BigInt mul = kingA;FOR(i, 1, n){ans = std::max(ans, mul / people[i].B);mul*=people[i].A;}ans.print();
}int main()
{run();return 0;
}

时间复杂度从O(n^2)降到了O(n log n)。

NOIP 2012 Senior 2 - 国王游戏相关推荐

  1. NOIP 贪心 国王游戏

    https://www.luogu.org/problemnew/show/P1080 题目描述 恰逢 HH国国庆,国王邀请nn 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整 ...

  2. 【题解】P1080 国王游戏(贪心+高精python天下第一)

    P1080 国王游戏 题目描述 恰逢 H国国庆,国王邀请n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排 ...

  3. P1080 国王游戏(贪心+大数乘除)

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

  4. P1080 国王游戏(贪心+高精度乘除及大数比较)

    https://www.luogu.org/problemnew/show/P1080 题目描述 恰逢 H 国国庆,国王邀请 n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数 ...

  5. P1080 国王游戏

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

  6. 洛谷P1080 国王游戏

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

  7. [NOIP2012] 提高组 洛谷P1080 国王游戏

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍 ...

  8. 贪心算法——国王游戏

    题目描述 孙悟空给花果山的小猴子们分桃子. 首先,他让每只小猴在左.右手上面分别写下一个整数,悟空自己也在左.右手上各写一个整数. 然后,让这 n 只小猴排成一排,悟空站在队伍的最前面. 排好队后,所 ...

  9. 国王游戏(贪心+模拟)

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

最新文章

  1. 从YARN迁移到k8s,滴滴机器学习平台二次开发是这样做的
  2. (转)Spring中ThreadLocal的认识
  3. 机器学习 —— 浅谈贝叶斯和MCMC
  4. 使用它tshark分析pcap的例子以及scapy下载地址
  5. oracle 如何修改字符集 update prop,ORACLE 修改字符集
  6. Oracle常用诊断事件清单
  7. 高等代数——大学高等代数课程创新教材(丘维声)——3.8笔记+习题
  8. 2021-11-07算法的本质是什么?
  9. html中放大镜案列,HTML放大镜的一种实现及原理讲解(js)
  10. java 发 腾讯企业邮_JAVA使用腾讯企业邮箱发送邮件时报错Could not connect to SMTP host...
  11. 2022年西式面点师(初级)考试题库及模拟考试
  12. 根据string查询是否是当月_发票勾选、查询、认证等25问!简直太全了!打印出来贴在桌子上学习!...
  13. iOS14隐私适配:根据不同的场景需求设置不同的定位精确度
  14. NameNode和DataNode中重要的数据结构解析
  15. uni-app安卓禁止截屏,一行代码
  16. 2019 kyle年度总结
  17. 用Python玩转PDF的各种骚操作
  18. 影视金曲-爱你一生嫌未够_昆仑奴插曲》
  19. Photoshop界面字体太小解决方案
  20. 苹果营收下降,但仍赚钱!

热门文章

  1. JQuery:$(...).ajaxSubmit is not a function
  2. Servlet和tomcat部署
  3. 使用MATLAB的residue()命令求传递函数的展开式
  4. 斐波拉契数列的三种实现方法
  5. 跨设备链路聚合 M-LAG配置案例
  6. 基于vue+element-ui的H5可视化编辑器
  7. seleniumxpath打码平台
  8. Linux开机无网络连接解决方案
  9. 5分钟理解Focal Loss与GHM
  10. 关于人工智能不会使大脑变懒惰的议论文_自律,拒绝懒惰和放纵,不枉余生