P1080 国王游戏

题目链接

P1080 国王游戏

题目梗概

现在有一位国王和N位大臣,排成一个队列。每个人左右手中各有整数a,b。队列中的每位大臣都可以获得国王奖励的金币,第i个大臣获得的金币数为 a(king)*a(1)*a(2)*…*a(i-1) / b(i)
所有大臣得到的金币数不完全一样,金币数肯定有最大值。
重新排列这些大臣的顺序,会改变这些大臣所得到的金币数,最大值也会有可能改变。
求在所有排列的最大金币值中,最小的是多少。

解题思路

贪心策略

本题的贪心策略比较不容易想到。我们可以像平常排列问题一样,先假定根据某种最优策略交换队列中某两个人位置后,得到的金币最大值更小。

此时,为简化问题便于分析,我们再进行一次假设,假定交换前后的两种队列的金币最大值都是在这两个人中,这样可以忽略到其他人。我们再计算出交换前两个人的金币数和交换后两个人的金币数,通过大小关系比较,可以得到ai*bi小的排在前面,金币最大值会更小。

贪心策略得到了,但不免想到,金币最大值肯定不总是在交换的两个人得到,那种情况下,上述的贪心策略还能够应用么。此时,我们可以对贪心策略进行证明,先假定队列已经按上述的贪心策略排列好,再推理得到交换前后各种情况的金币最大值,进行比较,贪心策略可以得证。

一个启示,在寻找贪心策略时,多做一些特殊性假设,某种程度上会易于得到贪心策略。

高精度问题

本题中,数据规模最大时,N为1000,a,b为10000,可以到在计算ai的乘积时,会超过long long 的数据规模,需要用数组来存储数字,此时就要涉及高精度(大数)的乘除法,即数组操作。

本题的高精度乘法为高精度乘以单精度,即大数(数组存储)乘以一个正常大小的数(int等数据类型)。方法可以结合下图自行理解。
高精度x单精度(引自oi-wiki.org)

高精度x单精度[引自oi-wiki.org]

除法则模拟手算除法中的竖式除法即可。

完整代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct minister{
	int left, right;
	long long left_mul_right; 
};
vector<int> product,coin,ans;
void mul(int x){
	//记录进位 
	int tmp = 0;
	//各个位置的数码直接乘以乘数X 
	for(int i = 0;i < product.size();++i) product[i] *= x;
	//处理进位 
	for(int i = 0;i < product.size();++i){
		product[i] += tmp;
		tmp = product[i]/10;
		product[i] %= 10;
		if(i == product.size()-1 && tmp != 0){
			product.push_back(0);
		}
	}
}

void div(int x){
	//初始化 商,默认长度等于被除数长度 
	coin.clear();
	coin.resize(product.size(),0);
	
	//记录当前被除数 
	int tmp = 0;
	for(int i = product.size()-1;i>=0;--i){
		tmp *= 10;
		tmp += product[i];
		//得到商和余 
		if(tmp >= x){
			coin[i] = tmp / x;
			tmp %= x;
		}
	}
	
	//去除最高位的0 
	while(coin.size() != 1 && coin.back()==0) coin.pop_back();
}

bool gt(vector<int> &a, vector<int> &b){
	if(a.size() != b.size()) return a.size() > b.size();
	else{
		for(int i = a.size() - 1;i >= 0;--i){
			if(a[i] != b[i]) return a[i] > b[i];
		}
		return true;
	}
}

bool cmp(minister a, minister b){
	return a.left_mul_right < b.left_mul_right;
}

int main(){
	int n;
	cin >> n;
	
	minister king;
	cin >> king.left >> king.right;
	
	vector<minister> m(n);
	for(int i = 0;i<n;++i){
		cin >> m[i].left >> m[i].right;
		m[i].left_mul_right = m[i].left * m[i].right;
	}
	
	sort(m.begin(),m.end(),cmp);

	while(king.left){
		product.push_back(king.left%10);
		king.left /= 10;
	}
	
	for(int i = 0;i<n;i++){
		div(m[i].right);
		mul(m[i].left);
		if(gt(coin, ans)) ans = coin;
	}
	
	for(int i = ans.size()-1;i >= 0;--i) cout << ans[i];
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值