NOIP 2012 Senior 2
来源:互联网 发布:python 移除数组元素 编辑:程序博客网 时间:2024/05/16 16:13
果然,一开始我又往DP方面想。还好,我及时发现了这个根本没法描述状态(就算用集合,也只是阶乘级到指数级,况且n<=1000,根本不可能),于是开始往贪心的方面想。
我们设左手上的数为A[i],右手上的数为B[i]
解1:
考虑到获得最多金币的大臣应该在最后一个,或者说,靠近最后一个。因为最后一个大臣计算金币时,分子是最大的,为其它大臣A的乘积。为什么说是靠近呢?因为有可能最后一个大臣的B[i]比倒数第二个大臣的B[i]大,导致获得的金币反而少。那我们先让最后一个大臣获得的金币尽量少试试。
第一次计算,可以计算其他人A[i]的积除以某个人的B[i],把最小的放在队列的最后,以后的计算把除了自己和计算过了的人的A[i]相乘,再除以这个人的B[i],同样把最小的放在后面。直到所有人都放完了就完了。
下面让我们简单证明下,为什么要把尽量小的放在后面:
设放在最后的大臣的左手为
L1 ,右手为R1 ,他的根据之前描述的算法算出来的金币最少。倒数第二个大臣的左手为L2 ,右手为R2 ,这个大臣是随机的,但是把他放在倒数第一的位置时获得的金币比最后的大臣多。再设倒数第三个到国王的左手的乘积为M ,则有:
COIN1=M∗L2R1 交换后有COIN2=MR2
COIN1=MR1 COIN2=M∗L1R2
交换后的COIN2 比COIN1 大,说明不会有更优解。那如果
COIN1=COIN2 呢? 可以用一个贪心策略:尽量让L 大的数放在后面,因为这样之前的L 的乘积会尽量小。
若此时COIN 和L 均相同,那么R 是相同的,随便选就可以了。最后的答案就是计算过程中出现的最大COIN 。可以再一次使用贪心。若在计算过程中COIN 的值非严格递减,则之后的COIN 也会递减,这时便已得到了答案,不用算完。
以上便是我做题时想到的思路。很幸运,居然得到了90分。其实以上思路并不严密。我们每次选择的都是放在当前位置
1 1 //国王1 11 1101 1191 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; } else break; vis[minIndex] = true; } ans.print();}int main(){ run(); return 0;}
解2
设相邻两个人分别为
i 和i+1 。左手数字为ai 和ai+1 ,右手数字为bi 和bi+1 。两人的金币数为wi 和wi+1 。
记Pi=a1∗a2∗a3∗...∗ai
可得:
wi=Pi−1bi
wi+1=Pibi+1
又Pi=Pi−1∗ai
那么wi+1=Pi−1∗aibi+1=wi∗ai∗bibi+1
发现,若使用递推,对于相邻的二元组(i,i+1) 来说,i+1 只受ai∗bi 影响,因此对ai ,bi 排序就可以了,相同的值不用加其它限制条件。
参考代码
#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 2012 Senior 5
- NOIP 2012 Senior 3
- NOIP 2011 Senior 2
- NOIP 2015 Senior 2
- NOIP 2014 Senior 2
- NOIP 2013 Senior 2
- NOIP 2016 Senior 2
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2011 Senior 4
- NOIP 2011 Senior 5
- NOIP 2011 Senior 6
- NOIP 2015 Senior 3
- NOIP 2015 Senior 5
- NOIP 2015 Senior 4
- beyond compare密钥被撤销的解决办法
- JavaScript小程序,大作用(函数)
- 【《Linus Torvalds自传-Linux OS 之父》摘录】
- spring-aop
- 字节序问题:大端还是小端
- NOIP 2012 Senior 2
- 凸包问题(Graham扫描法)
- 设计模式——单例(Java实现)
- C++ bitset使用教程
- JS控制浏览器全屏
- rm(操作系统的删除文件)与git rm的区别
- java装饰模式
- memmove的用法及实现
- linux基础命令——自我总结