NOIP 2012 Senior 2 - 国王游戏
果然,一开始我又往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∗L2R1COIN_1=\frac{M*L_2}{R_1}
COIN2=MR2COIN_2=\frac{M}{R_2} 交换后有
COIN1=MR1COIN_1=\frac{M}{R_1}
COIN2=M∗L1R2COIN_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−1biw_i=\frac {P_{i-1}}{b_i}
wi+1=Pibi+1w_{i+1}=\frac {P_i}{b_{i+1}}
又Pi=Pi−1∗aiP_i=P_{i-1}*a_i
那么wi+1=Pi−1∗aibi+1=wi∗ai∗bibi+1w_{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 - 国王游戏相关推荐
- NOIP 贪心 国王游戏
https://www.luogu.org/problemnew/show/P1080 题目描述 恰逢 HH国国庆,国王邀请nn 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整 ...
- 【题解】P1080 国王游戏(贪心+高精python天下第一)
P1080 国王游戏 题目描述 恰逢 H国国庆,国王邀请n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排 ...
- P1080 国王游戏(贪心+大数乘除)
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...
- P1080 国王游戏(贪心+高精度乘除及大数比较)
https://www.luogu.org/problemnew/show/P1080 题目描述 恰逢 H 国国庆,国王邀请 n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数 ...
- P1080 国王游戏
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...
- 洛谷P1080 国王游戏
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...
- [NOIP2012] 提高组 洛谷P1080 国王游戏
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍 ...
- 贪心算法——国王游戏
题目描述 孙悟空给花果山的小猴子们分桃子. 首先,他让每只小猴在左.右手上面分别写下一个整数,悟空自己也在左.右手上各写一个整数. 然后,让这 n 只小猴排成一排,悟空站在队伍的最前面. 排好队后,所 ...
- 国王游戏(贪心+模拟)
题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...
最新文章
- 从YARN迁移到k8s,滴滴机器学习平台二次开发是这样做的
- (转)Spring中ThreadLocal的认识
- 机器学习 —— 浅谈贝叶斯和MCMC
- 使用它tshark分析pcap的例子以及scapy下载地址
- oracle 如何修改字符集 update prop,ORACLE 修改字符集
- Oracle常用诊断事件清单
- 高等代数——大学高等代数课程创新教材(丘维声)——3.8笔记+习题
- 2021-11-07算法的本质是什么?
- html中放大镜案列,HTML放大镜的一种实现及原理讲解(js)
- java 发 腾讯企业邮_JAVA使用腾讯企业邮箱发送邮件时报错Could not connect to SMTP host...
- 2022年西式面点师(初级)考试题库及模拟考试
- 根据string查询是否是当月_发票勾选、查询、认证等25问!简直太全了!打印出来贴在桌子上学习!...
- iOS14隐私适配:根据不同的场景需求设置不同的定位精确度
- NameNode和DataNode中重要的数据结构解析
- uni-app安卓禁止截屏,一行代码
- 2019 kyle年度总结
- 用Python玩转PDF的各种骚操作
- 影视金曲-爱你一生嫌未够_昆仑奴插曲》
- Photoshop界面字体太小解决方案
- 苹果营收下降,但仍赚钱!
热门文章
- JQuery:$(...).ajaxSubmit is not a function
- Servlet和tomcat部署
- 使用MATLAB的residue()命令求传递函数的展开式
- 斐波拉契数列的三种实现方法
- 跨设备链路聚合 M-LAG配置案例
- 基于vue+element-ui的H5可视化编辑器
- seleniumxpath打码平台
- Linux开机无网络连接解决方案
- 5分钟理解Focal Loss与GHM
- 关于人工智能不会使大脑变懒惰的议论文_自律,拒绝懒惰和放纵,不枉余生