国王游戏.2333_微扰(邻项交换)证明

国王邀请大臣玩游戏,根据大臣左右手的数字确定奖赏金币数。目标是重新排列队伍,使最多奖赏尽可能少。贪心策略是按大臣左手数字升序排列,通过微扰法证明其正确性。最后一位大臣获得的金币为1*2*7/6=2。证明最优解是将大臣按左手数字从小到大排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 题目描述

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

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

输入

第一行包含一个整数 t,表示大臣的人数。

第二行包含两个整数 n 和 m,n,m之间用一个空格隔开,分别表示国王左手和右手上的整数。

接下来t 行,每行包含两个整数 x 和 y,x,y之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出

输出只有一行,包含一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

样例输入

3
1 1
2 3
7 4
4 6

样例输出

2

提示

  • 按 1、2、3 号大臣这样排列队伍,获得奖赏最多的大臣所获得金币数为2 ;

  • 按 1、3、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为2 ;

  • 按 2、1、3 这样排列队伍,获得奖赏最多的大臣所获得金币数为2;

  • 按 2、3、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9;

  • 按 3、1、2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2;

  • 按 3、2、1 这样排列队伍,获得奖赏最多的大臣所获得金币数为9 。

     

因此,奖赏最多的大臣最少获得 2个金币,答案输出2 。

  • 贪心

本题为贪心题目,大家可以先在网上查阅贪心相关内容,这里仅做简单介绍。

贪心是一种在每次决策时采取当前意义下最优策略的算法,使用贪心法要求问题的整体最优性可以由局部最优性导出。贪心算法的正确使用需要证明,常见手法有:1.微扰 2.范围缩放 3.决策包容性 4.反证法 5.数学归纳法。

有《算法进阶竞赛指南》可以查阅本书第45页。

  • 解题思路

在读题,并经过实际操作后,数据的处理是这样的(国王的右手无用,最后一位大臣的左手无用)即最后一个大臣得到的金币为1*2*7/6=2。

按照每个大臣左、右手上的数的乘积小到大排序,就是最优排队方案。这个贪心算法,可以使用微扰法证明。

对于任意一种顺序,设n名大臣左、右手上的数分别是A[1]~A[n]与B[1]~B[n],国王手里的数是A[0]和B[0]。

如果我们交换相邻的两个大臣i与i+1,交换前这两个大臣得到的金币分别是:
                        \frac{1}{B[i]}*\prod_{j=0}^{i-1}A[j]              与        \frac{1}{B[i+1]}*\prod_{j=0}^{i}A[j]

交换之后这两个大臣获得的奖励是:

                        \frac{1}{B[i+1]}*\prod_{j=0}^{i-1}A[j]       与        \frac{A[i+1]}{B[i]}*\prod_{j=0}^{i-i}A[j]

  • 参考答案

#include <bits/stdc++.h>
#define int long long
//将int定义更改为long long
#define x first
#define y second
using namespace std;
typedef pair<int,int>pii;
//将pair<int,int>更名为pii
//typedef可以更改很多,可以自行查阅
const int N=1010;
pii p[N];

//由于int已被修改为long long
//应用signed定义main函数返回类型
signed main()
{
    int n;
    cin>>n;
    for(int i=0;i<=n;i++)
    {
        int a,b;
        cin>>a>>b;
        //使得p[].x中存储a*b即大臣左右手乘积
        p[i].x=a*b,p[i].y=a;
    }
    //快排(按照pair结构中第一个出现的数,由小到大排序)
    sort(p+1,p+n+1);
    int res=0,ans=1;
    for(int i=0;i<=n;i++)
    {   //在计算乘积之前应注意的是,p[].x存储了左右手乘积,每次运算应首先除以该大臣右手
        if(i) res=max(res,ans/(p[i].x/p[i].y));
        ans=ans*p[i].y;
    }
    cout<<res<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊猫眼101

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值