您的位置:首页 > 其它

洛谷Oj-P1080 国王游戏-贪心+高精度

2018-03-10 14:55 141 查看
题目描述:

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

暴力枚举代码:

struct people
{
int left;//左手
int right;//右手
};
people p[1010];
int a[1010];//用来进行全排列的数组
int main()
{
int n;
cin >> n;//输入大臣的人数
cin >> p[0].left >> p[0].right;//国王的左手和右手
for(int i = 1; i <= n; ++i)
cin >> p[i].left >> p[i].right;//大臣的左手和右手
for(int i = 1; i <= n; ++i)//初始化下标序列
a[i] = i;
int b,max_coin,min_max_coin = inf;//左手的累乘
4000
,最多获得的奖赏,最多获得的奖赏的最小值
while(true)
{
max_coin = -inf;//重置
b = p[0].left;//国王的左手
for(int i = 1; i <= n; ++i)
{
max_coin = max(max_coin,b / p[a[i]].right);//取最大值
b = b * p[a[i]].left;//累乘
}
min_max_coin = min(min_max_coin,max_coin);//取最小值
if(next_permutation(a + 1,a + n + 1) == false)//进行全排列
break;
}
cout << min_max_coin << endl;//输出
return 0;
}


AC代码:

struct A
{
int l;
int r;
};
A a[1010];
//排在该大臣前面的所有人的左手上的数的乘积(以下简称乘积)
int m[5000],d[5000],max_coin[5000];//数组d为每位大臣所获得的金币数,数组max_coin为答案
bool cmp(const A &a,const A &b)//比较函数
{
return a.l * a.r < b.l * b.r;
}
bool compare(int a[],int b[])//判断a是否 >= b
{
if(a[0] > b[0])//先比较位数
return true;
if(a[0] < b[0])//先比较位数
return false;
for(int i = a[0]; i >= 1; --i)//若位数相同,则从最高位开始比较每一位的大小
{
if(a[i] > b[i])
return true;
if(a[i] < b[i])
return false;
}
return true;
}
void print(int a[])//a[1]为最低位,a[a[0]]为最高位,所以要逆序输出
{
for(int i = a[0]; i >= 1; --i)
cout << a[i];
cout << endl;
}
void multiply(int n)//低精度数n乘高精度数m
{
for(int i = 1; i <= m[0]; ++i)//m的每一位都乘n
m[i] = m[i] * n;
for(int i = 1; i <= m[0]; ++i)//处理进位
{
m[i + 1] += m[i] / 10;
m[i] %= 10;
}
m[0]++;
while(m[m[0]] >= 10)//处理最高位的进位
{
m[m[0] + 1] += m[m[0]] / 10;
m[m[0]] %= 10;
m[0]++;
}
if(m[m[0]] == 0)//特判
m[0]--;
return;
}
void divide(int n)//低精度数n除高精度数m,结果保存在高精度数d中
{
memset(d,0,sizeof(d));//初始化
int size = m[0];
int t = 0;
int flag = 1;
for(int i = size; i >= 1; --i)//从高位向低位
{
d[i] = (m[i] + 10 * t) / n;//商
t = (m[i] + 10 * t) % n;//余数项,迭代
if(d[i] != 0 && flag == 1)//确定商d的位数,遇到第一个不为0的位,并且一次性变量flag的值为1
{
d[0] = i;//则商有i位
flag = 0;//变量作废
}
}
return;
}
int main()
{
int n;
cin >> n;//输入大臣的人数
for(int i = 0; i <= n; ++i)//输入国王+大臣的左右手
cin >> a[i].l >> a[i].r;
sort(a + 1, a + n + 1,cmp);//排序以进行贪心
//初始化乘积
m[0] = 1;
m[1] = 1;
multiply(a[0].l);//乘上国王的左手上的数
for(int i = 1; i <= n; ++i)//对于每一位大臣
{
divide(a[i].r);//除以自己右手上的数,答案放在数组d中
multiply(a[i].l);//乘以自己左手上的数
if(compare(d,max_coin) == true)//如果所获得的金币更多,就更新答案
memcpy(max_coin,d,sizeof(d));//直接copy过去!
}
print(max_coin);
return 0;
}


解决方法:

因为这道题需要做乘积而且最极端的情况是10000^1000,即10^4000,需要写高精度。

高精度的题目需要好好练练了T_T,关键还是要动手模拟

题目要使获得奖赏最多的大臣,所获奖赏尽可能少

第一想法是暴力搜索,枚举全排列,把奖赏的最大值取个最小值,由于本人懒,暂且用next_permutation来代替搜索,结果无情地T掉

再看题目的问法,有点像二分答案

看了题解才知道是贪心。

为了简化证明,不妨假设只有两名大臣

人物左手右手
国王a0b0
大臣①a1b1
大臣②a2b2
此时大臣所获得的奖赏的最大值 ans1=max(a0b1,a0∗a1b2)

另一种情况为

人物左手右手
国王a0b0
大臣②a2b2
大臣①a1b1
此时大臣所获得的奖赏的最大值 ans2=max(a0b2,a0∗a2b1)

我们想大臣获得的奖赏的最大值尽可能小,就要选择最大值较小的那种排列方法

对上式替换可以得到

ans1=max(k1,k2)

ans2=max(k3,k4)

由于

a0∗a2b1>a0b1

a0∗a1b2>a0b2

所以

k2 > k3

k4 > k1

如果
ans1 < ans2


可以得到k4>k2(k3不如k2大,k4比k1大,只有k4比k2大才能满足ans2>ans1)



a0∗a1b2>a0∗a2b1

化简得

a1∗b1<a2∗b2

于是得出结论,如果想要大臣获得的奖赏的最大值尽可能小,就要将左手与右手的乘积较小的大臣放在队伍的前面

确定了排列顺序后,一轮模拟取最大值就可以了(此时的最大值便是所有最大值中最小的)

贪心中的排队问题可以用技巧来做分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: