当前位置: 首页 > news >正文

AcWing 114. 【0x07】国王游戏

题目描述

恰逢 HHH 国国庆,国王邀请 nnn 位大臣来玩一个有奖游戏。

首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。

然后,让这 nnn 位大臣排成一排,国王站在队伍的最前面。

排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:

排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

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

注意,国王的位置始终在队伍的最前面。

输入格式

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

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

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

输出格式

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

数据范围

1≤n≤10001 \le n \le 10001n1000
0<a,b<100000 < a,b <100000<a,b<10000

输入样例:
3
1 1
2 3
7 4
4 6
输出样例:
2

算法一.贪心 O(n2)O(n^2)O(n2)

这是一个非常典型的贪心问题。

我们记第 iii 个大臣左手上的数是 q[i].leftq[i].leftq[i].left,右手上的数是 q[i].rightq[i].rightq[i].right。针对于第 xxx 个大臣和第 x+1x+1x+1 个大臣,二者获得的奖赏分别为:

x,x+1:x,x+1:x,x+1:

  • xxx 个大臣:∏i=0x−1q[i].leftq[x].right=1q[x].right∏i=0x−1q[i].left\frac{\prod_{i=0}^{x-1}q[i].left}{q[x].right}=\frac{1}{q[x].right}\prod_{i=0}^{x-1}q[i].leftq[x].righti=0x1q[i].left=q[x].right1i=0x1q[i].left
  • x+1x+1x+1 个大臣:∏i=0xq[i].leftq[x+1].right=q[x].leftq[x+1].right∏i=0x−1q[i].left\frac{\prod_{i=0}^{x}q[i].left}{q[x+1].right}=\frac{q[x].left}{q[x+1].right}\prod_{i=0}^{x-1}q[i].leftq[x+1].righti=0xq[i].left=q[x+1].rightq[x].lefti=0x1q[i].left

如果第 xxx 个大臣和第 x+1x+1x+1 个大臣的位置发生了交换,二者获得的奖赏分别为:

x+1,x:x+1,x:x+1,x:

  • x+1x+1x+1 个大臣:∏i=0x−1q[i].leftq[x+1].right=1q[x+1].right∏i=0x−1q[i].left\frac{\prod_{i=0}^{x-1}q[i].left}{q[x+1].right}=\frac{1}{q[x+1].right}\prod_{i=0}^{x-1}q[i].leftq[x+1].righti=0x1q[i].left=q[x+1].right1i=0x1q[i].left
  • xxx 个大臣:∏i=0x−1q[i].left×q[x+1].leftq[x].right=q[x+1].leftq[x].right∏i=0x−1q[i].left\frac{\prod_{i=0}^{x-1}q[i].left×q[x+1].left}{q[x].right}=\frac{q[x+1].left}{q[x].right}\prod_{i=0}^{x-1}q[i].leftq[x].righti=0x1q[i].left×q[x+1].left=q[x].rightq[x+1].lefti=0x1q[i].left

因为四个数均 >0>0>0,那么去除 ∏i=0x−1q[i].left\prod_{i=0}^{x-1}q[i].lefti=0x1q[i].left 这个公因子不影响大小的比较;

据题意,我们要让获得最多奖赏的人获得的奖赏最少:

max(1q[x].right,q[x].leftq[x+1].right)max(\frac{1}{q[x].right},\frac{q[x].left}{q[x+1].right})max(q[x].right1,q[x+1].rightq[x].left)max(1q[x+1].right,q[x+1].leftq[x].right)max(\frac{1}{q[x+1].right},\frac{q[x+1].left}{q[x].right})max(q[x+1].right1,q[x].rightq[x+1].left) 进行比较,哪一个方案的值更小选哪个。

同时乘以 q[x].right×q[x+1].rightq[x].right×q[x+ 1].rightq[x].right×q[x+1].right 不影响大小比较结果,得:

max(q[x+1].right,q[x].right×q[x].left)max(q[x+ 1].right,q[x].right×q[x].left)max(q[x+1].right,q[x].right×q[x].left)max(q[x].right,q[x+1].left×q[x+1].right)max(q[x].right,q[x+1].left×q[x+1].right)max(q[x].right,q[x+1].left×q[x+1].right)

因为任意 q[i].rightq[i].rightq[i].rightq[i].leftq[i].leftq[i].left 均是正整数大于 111

q[x].right×q[x].left>q[x].rightq[x].right×q[x].left>q[x].rightq[x].right×q[x].left>q[x].rightq[x+1].left×q[x+1].right>q[x+1].rightq[x+1].left×q[x+1].right>q[x+ 1].rightq[x+1].left×q[x+1].right>q[x+1].right

实际上我们比较 q[x].right×q[x].leftq[x].right×q[x].leftq[x].right×q[x].leftq[x+1].left×q[x+1].rightq[x+1].left×q[x+1].rightq[x+1].left×q[x+1].right 的大小即可。

因此,当

  • q[x+1].left×q[x+1].rightq[x+1].left×q[x+1].rightq[x+1].left×q[x+1].right > q[x].right×q[x].leftq[x].right×q[x].leftq[x].right×q[x].left 时,x,x+1x,x+1x,x+1 方案好;

  • q[x].right×q[x].leftq[x].right×q[x].leftq[x].right×q[x].left > q[x+1].left×q[x+1].rightq[x+1].left×q[x+1].rightq[x+1].left×q[x+1].right 时,x+1,xx+1,xx+1,x 方案好;

综上所述,大臣的排序可以按照 q[i].right×q[i].leftq[i].right×q[i].leftq[i].right×q[i].left 的值从小到大升序排序。

依次顺序计算最大的奖赏即可,但是只能过 7/117/117/11 个测试点。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1050;
int n;
struct node {int left, right;
} q[N];bool cmp(node x, node y) { return x.left * x.right < y.left * y.right; }int main() {// freopen(".in","r",stdin);// freopen(".out","w",stdout);ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;cin >> q[0].left >> q[0].right;for (int i = 1; i <= n; i++) cin >> q[i].left >> q[i].right;sort(q + 1, q + n + 1, cmp);ll ans = -1;for (int i = 1; i <= n; i++) {ll num = 1;for (int j = 0; j < i; j++) {num *= q[j].left;}num /= q[i].right;ans = max(ans, num);}cout << ans;return 0;
}

算法二.贪心+高精度 $

针对于第 xxx 个大臣的奖赏,∏i=0x−1q[i].left\prod_{i=0}^{x-1}q[i].lefti=0x1q[i].left 需要高精度,∏i=0x−1q[i].leftq[x].right\frac{\prod_{i=0}^{x-1}q[i].left}{q[x].right}q[x].righti=0x1q[i].left 需要高精度;

因此开两个数组 num[N]num[N]num[N]ans[N]ans[N]ans[N] 高精度数组;

  • num[N]num[N]num[N] 存每一个大臣的奖赏,即 ∏i=0x−1q[i].leftq[x].right\frac{\prod_{i=0}^{x-1}q[i].left}{q[x].right}q[x].righti=0x1q[i].left
  • ans[N]ans[N]ans[N]nnn 个大臣的奖赏的最大值。
C++ 代码(已将低精度代码更换为了高精度,高精度算法细节不再解释)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1050;
const int M = 40000;
int n, ans[M], len_a, num[M], len_n;
struct node {int left, right;
} q[N];bool cmp(node x, node y) { return x.left * x.right < y.left * y.right; }void cul_1(int x) {	//num *= xfor (int i = 0; i < len_n; i++) {num[i] *= x;}for (int i = 0; i < len_n - 1; i++) {num[i + 1] += num[i] / 10;num[i] %= 10;}while (num[len_n - 1] >= 10) {num[len_n] = num[len_n - 1] / 10;num[len_n - 1] %= 10;len_n++;}
}void cul_2(int x) {//num /= xint temp[M], len_t = 0, t = 0;for (int i = len_n - 1; i >= 0; i--) {t = 10 * t + num[i];temp[len_t++] = t / x;t %= x;}len_n = len_t;memset(num, 0, sizeof(num));for (int i = 0; i < len_t; i++) {num[len_t - 1 - i] = temp[i];}
}bool com() {	//if(num > ans)if (len_a != len_n)return len_n > len_a;for (int i = len_a - 1; i >= 0; i--) {if (num[i] < ans[i]) {return false;}if (num[i] > ans[i]) {return true;}}return false;
}void cul_3() { //ans = nummemset(ans, 0, sizeof(ans));len_a = len_n;for (int i = 0; i < len_n; i++) {ans[i] = num[i];}
}void getout_a() {	//cout << ans;while (ans[len_a - 1] == 0 && len_a > 1) len_a--;for (int i = len_a - 1; i >= 0; i--) cout << ans[i];
}int main() {// freopen(".in","r",stdin);// freopen(".out","w",stdout);ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;cin >> q[0].left >> q[0].right;for (int i = 1; i <= n; i++) cin >> q[i].left >> q[i].right;sort(q + 1, q + n + 1, cmp);for (int i = 1; i <= n; i++) {memset(num, 0, sizeof(num));num[0] = 1, len_n = 1;	//num = 1;for (int j = 0; j < i; j++)cul_1(q[j].left);	//num *= q[j].left;cul_2(q[i].right);	//num /= q[i].right;if (com())	cul_3();	//if(num > ans)	ans = num;}getout_a();	//cout << ans;return 0;
}

但是只能过 8/118/118/11 个测试点。

算法三.贪心+高精度+优化

我们发现,针对于每一个大臣,都会计算前面所有人的左手成绩,即:

当我们计算第 xxx 个大臣前面的 ∏i=0x−1q[i].left\prod_{i=0}^{x-1}q[i].lefti=0x1q[i].left 时,其实在 计算第 x−1x-1x1 个大臣时,就完成了 ∏i=0x−2q[i].left\prod_{i=0}^{x-2}q[i].lefti=0x2q[i].left

即左手的累乘结果完全可以避免大量的重复计算,当第 xxx 个大臣要使用时,只需要对于已有的 ∏i=0x−2q[i].left\prod_{i=0}^{x-2}q[i].lefti=0x2q[i].left 乘上一个 q[x−1].leftq[x-1].leftq[x1].left 即可。

C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1050;
const int M = 40000;
int n, ans[M], len_a, num[M], sum[M], len_s, len_n;
struct node {int left, right;
} q[N];bool cmp(node x, node y) { return x.left * x.right < y.left * y.right; }void cul_1(int x) {for (int i = 0; i < len_n; i++) num[i] *= x;for (int i = 0; i < len_n - 1; i++) {num[i + 1] += num[i] / 10;num[i] %= 10;}while (num[len_n - 1] >= 10) {num[len_n] = num[len_n - 1] / 10;num[len_n - 1] %= 10;len_n++;}
}void cul_2(int x) {int temp[M], len_t = 0, t = 0;for (int i = len_n - 1; i >= 0; i--) {t = 10 * t + num[i];temp[len_t++] = t / x;t %= x;}len_s = len_t;memset(sum, 0, sizeof(sum));for (int i = 0; i < len_t; i++) sum[len_t - 1 - i] = temp[i];while (sum[len_s - 1] == 0 && len_s > 1) len_s--;
}bool com() {if (len_a != len_s)return len_s > len_a;for (int i = len_a - 1; i >= 0; i--) {if (sum[i] < ans[i])	return false;if (sum[i] > ans[i])	return true;}return false;
}void cul_3() {memset(ans, 0, sizeof(ans));len_a = len_s;for (int i = 0; i < len_s; i++)ans[i] = sum[i];
}void getout_a() {while (ans[len_a - 1] == 0 && len_a > 1) len_a--;for (int i = len_a - 1; i >= 0; i--) cout << ans[i];
}int main() {// freopen(".in","r",stdin);// freopen(".out","w",stdout);ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n;cin >> q[0].left >> q[0].right;for (int i = 1; i <= n; i++) cin >> q[i].left >> q[i].right;sort(q + 1, q + n + 1, cmp);num[0] = 1, len_n = 1;for (int i = 1; i <= n; i++) {cul_1(q[i - 1].left);cul_2(q[i].right);if (com())	cul_3();}getout_a();return 0;
}

可以过 11/1111/1111/11 个数据点,得到 AcceptedAcceptedAccepted

http://www.dtcms.com/a/347386.html

相关文章:

  • C代码学习笔记(一)
  • Windows打开命令窗口的几种方式
  • 使用 PSRP 通过 SSH 建立 WinRM 隧道
  • 注意力机制中为什么q与k^T相乘是注意力分数
  • 每日定投40刀BTC(22)20250802 - 20250823
  • 编程刷题-染色题DFS
  • 03_数据结构
  • 在 CentOS 7 上搭建 OpenTenBase 集群:从源码到生产环境的全流程指南
  • MSPM0G3507工程模板创建
  • 微信小程序自定义组件开发(上):从创建到数据通信详解(五)
  • 纠删码技术,更省钱的分布式系统的可靠性技术
  • 使用springboot开发-AI智能体平台管理系统,统一管理各个平台的智能体并让智能体和AI语音设备通信,做一个属于自己的小艾同学~
  • Dubbo vs Feign
  • 个人思考与发展
  • 探秘北斗卫星导航系统(BDS):架构、应用与未来蓝图,展现中国力量
  • 详细说一说JIT
  • Redis面试精讲 Day 28:Redis云原生部署与Kubernetes集成
  • Js逆向 拼夕夕anti_content
  • 深入解析Spring Boot自动配置原理:简化开发的魔法引擎
  • Java基础第2天总结
  • 青少年机器人技术(四级)等级考试试卷-实操题(2021年12月)
  • 互联网大厂Java面试实战:核心技术栈与场景化提问解析(含Spring Boot、微服务、测试框架等)
  • Java 遗传算法在中药药对挖掘中的深度应用与优化策略
  • 雨雾天气漏检率骤降80%!陌讯多模态车牌识别方案实战解析
  • Redis--day10--黑马点评--秒杀优化消息队列
  • 【JavaEE】多线程 -- JUC常见类和线程安全的集合类
  • 什么猫粮好?2025最新猫粮排名合集
  • 深度解析Bitmap、RoaringBitmap 的原理和区别
  • MySql知识梳理之DDL语句
  • TypeScript 类型系统入门:从概念到实战