[NOIP2012 提高组] 国王游戏(C++,贪心,高精度)

2023-05-16

题目描述

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

3 
1 1 
2 3 
7 4 
4 6

样例输出 #1

2

提示

【输入输出样例说明】

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

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

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

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

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

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

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

【数据范围】

对于 20 % 20\% 20% 的数据,有 1 ≤ n ≤ 10 , 0 < a , b < 8 1≤ n≤ 10,0 < a,b < 8 1n10,0<a,b<8

对于 40 % 40\% 40% 的数据,有$ 1≤ n≤20,0 < a,b < 8$;

对于 60 % 60\% 60% 的数据,有 1 ≤ n ≤ 100 1≤ n≤100 1n100

对于 60 % 60\% 60% 的数据,保证答案不超过 1 0 9 10^9 109

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 , 000 , 0 < a , b < 10000 1 ≤ n ≤1,000,0 < a,b < 10000 1n1,000,0<a,b<10000

解题思路:

先不考虑如何使最大值尽可能的小,只针对某一位固定的大臣

为了使这位大臣所获得的金币数最少,我们希望他前面所有人左手上的数乘积尽可能的小,同时这位大臣右手上的数尽可能的大

要使乘积尽可能的小,需要使乘数尽可能的少、使排队的人左手上的数尽可能的小

所以现在有三个目的:该大臣前面的人数尽可能的少,前面的人左手上的数尽可能的小,他右手上的数尽可能的大

以上三条就是减少一位大臣获得奖赏的所有方法

然后我们来考虑如何使最大值尽可能的小

对于第一条,只需要让最大值站在国王后面就可以了,但这显然不合理

对于第二条,前面的人左手上的数尽可能的小,可以说就是后面的人左手上的数尽可能的大

也就是说左手数越大的大臣,我们偏向于让他站在后面

对于第三条,我们偏向于让右手数越大的大臣站在越后面

那么是不是左右手乘积越大站在越后面就行了呢?接下来进行证明

现假设有n位大臣,编号分别为 p 1 , p 2 , … , p n p_1,p_2,\dots,p_n p1,p2,,pn,其中获得奖赏最多的为 p k ( 1 ≤ k ≤ n ) p_k(1 \leq k \leq n) pk(1kn)

要使max减少,需要进行位置交换,假设将 p i , p j ( 1 ≤ i ≤ k ≤ j ≤ n , i ≠ j ) p_i,p_j(1 \leq i \leq k \leq j \leq n,i \ne j) pi,pj(1ikjn,i=j)进行交换可以使max减少

可以看到,对于所有 p i p_i pi之前和 p j p_j pj之后的大臣,其获得的金币数不受影响

设左手数数组、右手数数组分别为left[n + 1],right[n + 1]

接下来分类讨论三种情况

(1)i != k && j != k

由于 p k p_k pk的右手数不会改变,前方人数不变,max减少一定是因为第二条

可以看到, p i + 1 p_{i+1} pi+1~ p j − 1 p_{j-1} pj1所获得的奖赏均减少

同时由于 p j p_j pj的右手数不变,且因为第一条,所以 p j p_j pj获得金币数减少

要使max减少,只需要使 p i ≤ m a x p_i \leq max pimax p k p_k pk即可

可以推导出 n e w p i = l e f t 1 ∗ ⋯ ∗ l e f t j − 1 ∗ l e f t j l e f t i r i g h t j ∗ r i g h t i r i g h t j = o l d p j ∗ l e f t j ∗ r i g h t j l e f t i ∗ r i g h t i newp_i = \frac{left_1* \dots *left_{j-1}* \frac{left_j}{left_i}}{right_j* \frac{right_i}{right_j}} = oldp_j * \frac{left_j * right_j}{left_i * right_i} newpi=rightjrightjrightileft1leftj1leftileftj=oldpjleftirightileftjrightj(注:这个式子除了 i < j i < j i<j之外没有任何限制)

若有 p i p_i pi左右手乘积更大,则有 n e w p i < o l d p j ≤ m a x newp_i < oldp_j \leq max newpi<oldpjmax

(2)i == k && j != k

由于 p k p_k pk的右手数不会改变,前方大臣左手数乘积增大,显然不可能使max减小

(3)i != k && j == k

显然max减少是因为第一条,有 r i g h t k > r i g h t i right_k > right_i rightk>righti

由(1)式子可得,如果 p i p_i pi的左右手乘积更大, n e w p i < o l d p k ≤ m a x newp_i < oldp_k \leq max newpi<oldpkmax

l e f t k ≤ l e f t i left_k \leq left_i leftklefti,则 p i + 1 p_{i+1} pi+1~ p k − 1 p_{k-1} pk1所获得的的奖赏均减少,故max减少了

l e f t k > l e f t i left_k > left_i leftk>lefti,显然 p k p_k pk的左右手乘积更大,不能和 p i p_i pi交换

至此,证明完毕

接下来是代码的实现,注意可能会出现 100 0 9999 1000^{9999} 10009999故需要使用高精度

#include <iostream>
#include <algorithm>
#include <iomanip>
//#include <fstream>//Debug
using namespace std;

const size_t bits = 10;//1e10进制
const int64_t mod_num = 10000000000;//1e10

struct person {
	int left;
	int right;
};

person queue[1001];
int64_t num[2][10000] = { {1},{1} };//10000 * bits位
int64_t max_num[10000] = { 0 };//2^63 - 1 = 9 223 372 036 854 775 807
int max_len = 0;
//int max_i;//Debug

int main() {
	//ifstream ifs("C:\\Users\\dell\\Downloads\\P1080_9.in", ios::in);//Debug
	int n, highest = 0;
	cin >> n;
	//ifs >> n;//Debug
	for (int i = 0; i <= n; i++)
		cin >> queue[i].left >> queue[i].right;
		//ifs >> queue[i].left >> queue[i].right;//Debug
	//ifs.close();//Debug
	sort(queue + 1, queue + n + 1, [](person p_1, person p_2) {
		return p_1.left * p_1.right < p_2.left * p_2.right;
		});

	for (int i = 1; i <= n; i++) {
		//if (i == 1000) system("pause");//Debug
		num[(i + 1) % 2][0] = int64_t(queue[i - 1].left) * num[i % 2][0];
		int j = 1;
		while (j <= highest + 1)
		{
			num[(i + 1) % 2][j] = int64_t(queue[i - 1].left) * num[i % 2][j];
			num[(i + 1) % 2][j] += num[(i + 1) % 2][j - 1] / mod_num;
			num[(i + 1) % 2][j - 1] %= mod_num;
			j++;
		}

		while (j > 0 && num[(i + 1) % 2][j] == 0) j--;
		highest = j;
		int z = j;
		int64_t upper = 0, lower = 0;
		while (z >= 0) {
			upper = (num[(i + 1) % 2][z] + lower) / int64_t(queue[i].right);
			lower = (num[(i + 1) % 2][z] + lower) % int64_t(queue[i].right) * mod_num;
			if (upper > max_num[z]) {
				max_len = j;
				//max_i = i;//Debug
				lower = 0;
				while (j >= 0) {
					upper = (num[(i + 1) % 2][j] + lower) / int64_t(queue[i].right);
					lower = (num[(i + 1) % 2][j] + lower) % int64_t(queue[i].right) * mod_num;
					max_num[j] = upper;
					j--;
				}
				break;
			}
			else if (upper == max_num[z]) {
				z--;
				continue;
			}
			else break;
		}
	}

	//cout << max_i << endl;//Debug
	cout << max_num[max_len];
	while (--max_len >= 0) cout << setiosflags(ios::right) << setfill('0') << setw(bits) << max_num[max_len];
}

我的高精度稍微改了一下,不想看的这段可以直接跳过

改动的第一个地方就是把10进制改成了1e10进制

这样好处就是可以把大臣手上的数看成一位(因为一定小于1e10),并减少循环次数

需要做出的改变就是模数由10变为1e10,在输出的时候要格式化一下

改动的第二个地方就是使用两个高精度数字进行高精度乘法(也就是%2的意义,i每改变奇偶性一次,乘积和乘数交换一次)

这样好处就是节约了空间

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[NOIP2012 提高组] 国王游戏(C++,贪心,高精度) 的相关文章

  • Ubuntu(虚拟机)的Anaconda 及使用

    安装Anaconda 使用firefox打开Ananconda网址Anaconda The World 39 s Most Popular Data Science Platform 下载后有 sh文件 xff1a Anaconda3 20
  • android 10.0 SystemUI屏蔽某个app的通知

    1 概述 在10 0的系统产品开发中 产品有需求 需要状态栏不显示某个app的通知 根据SystemUI源码通知显示流程可以得知NoticationFilter java中可以处理过滤通知 2 SystemUI屏蔽某个app的通知的核心类
  • 如何从windows host快速访问wsl文件夹

    背景 习惯在linux环境做开发活动 但也喜欢windows生态下的很多软件 如 web开发 xff0c 在windows下做视频 图片 文档编写等工作 qt开发 xff0c qt linguist在windows下原生支持简体中文 pyt
  • mysql分组查询

    概念 分组查询主要是用来统计的 xff0c 一般都是按照某一个列进行统计分组 统计类型 xff1a 求平均 xff0c 求最大 xff0c 求最小 xff0c 求和等等 分组查询需要结合分组函数一起完成 xff0c 常用的分组函数 xff1
  • Ubuntu 22.04自动挂起后无法唤醒

    可实现在键盘 鼠标断电后的唤醒 xff0c 前提是合上笔记本 但是为了以防万一 xff0c 建议在设置中将挂起有关选项全部关闭 一 安装 xff08 这个不知道干嘛的 xff09 sudo apt get install pm utils
  • C语言入门——1000以内的完数

    完数定义 如果一个数恰好等于它的真因子之和 xff0c 则称该数为 完全数 2 各个小于它的约数 xff08 真约数 列出某数的约数 xff0c 去掉该数本身 xff0c 剩下的就是它的真约数 xff09 的和等于它本身的自然数叫做完全数
  • 利用数组进行排序(选择排序)

    排序过程 1 首先通过n 1次比较 xff0c 从n个数中找出最小的 xff0c 将它与第一个数交换 第一趟选择排序 xff0c 结果最小 的数被安置在第一个元素位置上 xff08 2 xff09 再通过n 2次比较 xff0c 从剩余的n
  • C语言 : 矩阵转置 (二维数组)

    题目描述 xff1a 输入N N的矩阵 xff0c 输出它的转置矩阵 矩阵的转置操作 xff0c 即把矩阵的行元素变为列元素 列元素变为行元素的过程 输入 xff1a 第一行为整数N xff0c 接着是一个N N的矩阵 输出 xff1a 转
  • C++打卡12-百鸡百钱

    一 实验目标 公鸡1只5钱 xff0c 母鸡1只3钱 xff0c 小鸡3只1钱 xff0c 用百钱买百鸡 xff0c 问有几种购买的方案 xff1f 输入格式 输入n和m 表示用n钱买m只鸡 输出格式 输出购买的方案数 输入 100 100
  • 【C语言】十六进制转换为十进制

    目录 题目描述 补充知识 xff1a 算法分析 优化算法 写在最后 题目描述 输入一个十六进制数字串 xff0c 将其转换成为对应的整数并输出转换结果 xff0c 遇到非十六进制数字或字符串结束符 xff08 39 0 39 xff09 结
  • C语言程序入门之基本数据类型、常量与变量、运算符

    目录 一 基本数据类型 1 整型 2 浮点型 3 字符型 二 常量与变量 1 常量 2 变量 三 运算符 1 算术运算符 2 关系运算符 3 逻辑运算符 4 位运算符 5 自增自减运算符 6 赋值运算符 7 逗号运算符 8 条件运算符 9
  • C语言入门之分支与循环

    目录 一 分支语句 1 if语句 三种形式 if语句的嵌套 2 switch 二 循环语句 1 while语句 2 do while语句 3 for语言 一 分支语句 分支语句又叫选择结构语句 xff0c C语言中 xff0c 选择结构语句
  • Android 10.0 系统设置开启始终在后台运行的权限

    android 6 0系统中保活机制 所以在系统内存不够的时候 后台运行的app有可能会被系统杀掉 所以为了让app不能系统杀掉保持永久运行 就必须要增加权限 把app 添加到保活白名单里面 或者授予后台运行的权限 接下来看Settings
  • C语言之数组

    目录 一 一维数组 1 一维数组的定义 2 一维数组初始化 3 一维数组的引用 4 一维数组程序举例 二 二维数组 1 二维数组的定义 2 二维数组的初始化 3 二维数组的引用 4 二维数组的举例 三 字符数组 1 字符数组的定义 2 字符
  • C语言之函数

    目录 一 函数的定义 二 函数的参数 1 实际参数 xff08 实参 xff09 2 形式参数 xff08 形参 xff09 三 函数的调用 四 函数的返回 五 函数的声明 一 函数的定义 函数是一块代码 xff0c 接受零个或多个参数 x
  • C语言之指针运算符、指针变量及其定义、指针的使用

    目录 一 指针运算符 1 amp 运算符 2 运算符 二 指针变量及其定义 1 指针变量 2 定义指针变量 三 指针的使用 指针 xff0c 是C语言中的一个重要概念 xff0c 也是掌握C语言比较困难的部分 指针也就是内存地址 xff0c
  • C语言——指针的运算以及野指针

    目录 一 野指针 1 野指针成因 xff08 1 xff09 指针未初始化 xff08 2 xff09 指针越界访问 xff08 3 xff09 指针指向的空间释放 2 如何规避野指针 二 指针的运算 1 赋值运算 2 算术运算 3 关系运
  • C语言错题总结

    输出格式 xff08 以整形为例 xff0c 其他类似 xff09 xff1a d是普通的输出 5d是将数字按宽度为5 xff0c 采用右对齐方式输出 xff0c 若数据位数不到5位 xff0c 则左边补空格 xff0c 若数据位数超过5位
  • C语言小游戏之弹跳的小球

    1 显示静止的小球 首先利用printf函数在屏幕坐标 xff08 x y xff09 处显示一个静止的小球字符 39 o 39 xff0c 应当注意屏幕坐标系的原点在左上角 xff0c 代码如下 xff1a include lt stdi
  • 解决Clash意外关闭后的问题;附clash常见问题解决办法

    一 引言 最近 xff0c 我在一次win11的重启更新后遇到了一个问题 xff0c 那就是发现我的浏览器无法上网了 起初 xff0c 我以为我的网络存在问题 xff0c 但后来发现不是这个问题 经过我查阅资料以及实际操作后 xff0c 我

随机推荐

  • Qt 获取所有进程、终止某个进程

    代码中用到Qt库的地方 xff0c 不使用Qt库的可以替换为自己相应的函数 方法一 xff1a 1 Qt开源库 xff0c 通过QProcess启动系统命令 tasklist exe 获取正在运行的进程 2 QProcess process
  • Qt QPixmap设置图片透明度

    最近看到美图秀秀的一些功能 xff0c 可以手动设置图片的透明度并显示在其它图片上 xff0c 所以自己动手做了个小Demo xff0c 实际效果如下 xff1a xff08 图片仅供参考使用 xff09 可以看到拖动下方进度条 xff0c
  • 【IDEA报错】Failed to start bean ‘documentationPluginsBootstrapper‘问题及解决方案

    使用springfox swagger2进行接口文档输出 编写配置文件Swagger2Config 64 Configuration 64 EnableSwagger2 public class Swagger2Config 64 Bean
  • Android 调整Spinner下拉框高度(避免下拉列表跑到顶部)

    在运用系统原生的Spinner控件做下拉选择功能时 由于选择项的子项Item太多 导致下拉列表跑到上面去了 关键原因是系统下拉默认的高度 spinner所在的位置 超过了屏幕底部的高度 所以就会出现下拉列表跑到控件的头部去了 解决方案 1
  • c++中的随机数rand()

    总结 xff1a 1 RANK MAX 61 32767 2 随机范围 xff1a num 61 rank x C 43 43 中rand 函数的用法 1 rand 不需要参数 xff0c 它会返回一个从0到最大随机数的任意整数 xff0c
  • 最大数和最小数位置交换位置

    输入10个整数 xff0c 用函数编程将其中最大数与最小数的位置互换 然后在主函数中将交换后的数组的所有元素输出 include lt stdio h gt int main int arr 10 61 0 int maxi 61 0 in
  • HDFS基本概念

    目录 零 学习目标 一 导入新课 二 新课讲解 xff08 一 xff09 HFDS的演变 xff08 二 xff09 HDFS的基本概念 1 NameNode xff08 名称节点 xff09 2 DataNode xff08 数据节点
  • 用栈来判断字符串是否回文

    include lt iostream gt include lt bits stdc 43 43 h gt using namespace std define MAX SIZE 100 class Stack private char
  • 技术分享 | 将覆盖反馈融入黑盒模糊测试技术提升测试效率

    引言 近几年来 xff0c 自动化漏洞挖掘技术成为网络安全的重要研究方向 传统的漏洞挖掘技术面临着耗时长 误报多等痛点 xff0c 且无法全面地探测目标软件中的已知与未知漏洞 因此 xff0c 一种简单高效的漏洞挖掘技术 xff0c 即模糊
  • 基于Vue3+Vite实现的移动端天气预报系统

    文章目录 1 前言2 准备工作3 项目创建与配置3 1适配移动端3 2路由配置 4 功能实现4 1Footer组件的实现4 2Mine组件的实现4 3Guide组件的实现4 4GuideInfo组件的实现4 5 Home组件的实现4 6封装
  • 【C++ 将十六进制数转换为二进制数】

    问题描述 将十六进制数转换为二进制数 输入格式 输入一个16进制数 输出格式 输出二进制数 输入样例 在这里给出一组输入 例如 xff1a 23 输出样例 在这里给出相应的输出 例如 xff1a 100011 输入样例 在这里给出一组输入
  • 电力系统强大的Gurobi 求解器的学习(Python&Matlab)

    到底有多强大 xff0c 看看就知道 xff0c 必须 x1f44d x1f44d x1f44d xff1a 目录 1 概述 2 算例理解 Python 2 1 算例1 详细入门 2 2 算例2 一般线性规划问题 2 3 算例3 非凸问题
  • 位移操作符 <<左移 与 >>右移 的基本逻辑

    1 xff1a lt lt 左移操作符 2 xff1a gt gt 右移操作符 xff08 注 xff1a 位移操作符的操作数只能是整数 xff09 lt lt 左移操作符 与 gt gt 右移操作符 都是移二进制位操作符 整数的二进制表现
  • 判断101到200有多少素数,并输出所有素数

    分析 xff1a 1 从101到200 xff0c 我们要用到for语句 xff08 如果有其它条件就把i 61 101到200改成条件的数 xff09 for i 61 101 i lt 61 200 i 43 43 2 判断素数 xff
  • 求最大值,求10 个整数中最大值

    求最大值 求10 个整数中最大值 思路 xff1a 1 采用循环的方式输入一个数组 2 使用max标记数组中的最大值 xff0c 采用循环的方式依次获取数组中的每个元素 xff0c 与max进行比较 xff0c 如果arr i 大于 max
  • Android app后台运行休眠仍然可以运行的方法(确保一直运行)

    在播放器app中由于需要用后台service 来播放音乐 所以一旦进入休眠状态时 就有可能被杀掉进程 所以需要让service 一直运行不被杀掉进程 在android 中WakeLock的相关 API可以确保应用程序中后台任务一直运行 使应
  • 输入N个数,输出最大值和最小值

    include lt stdio h gt int main int max min a b c num scanf 34 d 34 amp a scanf 34 d 34 amp b max 61 b min 61 b for c 61
  • 【C语言学习】数组排序.选择法

    上课学的选择法数组排序 xff0c 老师讲的云里雾里的 xff0c 准备用自己的理解再写一下它的原理及注意点 xff0c 希望对你有所帮助 目录 1 原理 2 注意点 3 代码 1 原理 每一次从待排序的数据元素中选出最小 或最大 的一个元
  • 用C语言,求10个数的最小值和最大值

    用数组a存放10个数 xff0c min max存放最小值和最大值 对数组进行遍历 将a 0 设为最小值和最大值的初值 xff1b 利用a i 和min max进行比较 include lt stdio h gt main int i a
  • [NOIP2012 提高组] 国王游戏(C++,贪心,高精度)

    题目描述 恰逢 H 国国庆 xff0c 国王邀请 n n n 位大臣来玩一个有奖游戏 首先 xff0c 他让每个大臣在左 右手上面分别写下一个整数 xff0c 国王自己也在左 右手上各写一个整数 然后 xff0c 让这 n n