设按新安排的队伍顺序第 iii 个人左手数字为 aia_iai,右手数字为 bib_ibi。
我们考虑什么时候交换第 iii 个人和第 i+1i+1i+1 个人不会更优。
设前 i−1i-1i−1 个人获得的金币的最大值为 preprepre,他们 aia_iai 的乘积为 sss。
由于交换第 iii 个人和第 i+1i+1i+1 个人对后面的人获得的金币数没有影响,所以我们只需要让前 i+1i+1i+1 个人的最大值最小。
交换前:ans1=max(pre,sbi,s⋅aibi+1)ans_1=\max\left(pre,\dfrac{s}{b_i},\dfrac{s\cdot a_i}{b_{i+1}}\right)ans1=max(pre,bis,bi+1s⋅ai)。
交换后:ans2=max(pre,sbi+1,s⋅ai+1bi)ans_2=\max\left(pre,\dfrac{s}{b_{i+1}},\dfrac{s\cdot a_{i+1}}{b_i}\right)ans2=max(pre,bi+1s,bis⋅ai+1)。
交换第 iii 个人和第 i+1i+1i+1 个人不会更优当且仅当 ans1≤ans2ans_1\leq ans_2ans1≤ans2。
显然有 sbi≤s⋅ai+1bi\dfrac{s}{b_i}\leq \dfrac{s\cdot a_{i+1}}{b_i}bis≤bis⋅ai+1,sbi+1≤s⋅aibi+1\dfrac{s}{b_{i+1}}\leq \dfrac{s\cdot a_i}{b_{i+1}}bi+1s≤bi+1s⋅ai,所以 ans1≤ans2ans_1\leq ans_2ans1≤ans2 的充分条件是 s⋅aibi+1≤s⋅ai+1bi\dfrac{s\cdot a_i}{b_{i+1}}\leq \dfrac{s\cdot a_{i+1}}{b_i}bi+1s⋅ai≤bis⋅ai+1。
得到 aibi≤ai+1bi+1a_ib_i\leq a_{i+1}b_{i+1}aibi≤ai+1bi+1,于是我们对所有人按 aibia_ib_iaibi 排序即可。
貌似这里只考虑了相邻的交换,那么这个排队顺序为什么一定是最优的呢?我们可以这么想:如果这个排队顺序中存在某个 aibi>ai+1bi+1a_ib_i>a_{i+1}b_{i+1}aibi>ai+1bi+1,那么交换 iii 和 i+1i+1i+1 肯定是不劣的。所以最优排队顺序中一定对于所有的 iii 都满足 aibi≤ai+1bi+1a_ib_i\leq a_{i+1}b_{i+1}aibi≤ai+1bi+1。
需要高精度。
#include<bits/stdc++.h>
#define N 1010
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
struct data
{
int a,b;
}p[N];
bool operator < (data a,data b)
{
return a.a*a.b<b.a*b.b;
}
struct BigInt
{
int len,a[4010];
BigInt(){len=0;memset(a,0,sizeof(a));};
BigInt(int x)
{
len=0;
while(x)
{
a[++len]=x%10;
x/=10;
}
}
};
bool operator < (const BigInt &a,const BigInt &b)
{
if(a.len!=b.len) return a.len<b.len;
for(int i=a.len;i>=1;i--)
if(a.a[i]!=b.a[i]) return a.a[i]<b.a[i];
return 0;
}
BigInt operator * (BigInt a,int b)
{
for(int i=1;i<=a.len;i++) a.a[i]*=b;
int i;
for(i=1;i<=a.len||a.a[i];i++)
{
a.a[i+1]+=a.a[i]/10;
a.a[i]%=10;
}
a.len=i;
return a;
}
BigInt operator / (BigInt a,int b)
{
int now=0;
for(int i=a.len;i>=1;i--)
{
now=now*10+a.a[i];
a.a[i]=now/b;
now%=b;
}
while(a.len>1&&!a.a[a.len]) a.len--;
return a;
}
void print(const BigInt &a)
{
for(int i=a.len;i>=1;i--)
putchar('0'+a.a[i]);
}
int n,a,b;
int main()
{
n=read(),a=read(),b=read();
for(int i=1;i<=n;i++)
p[i].a=read(),p[i].b=read();
sort(p+1,p+n+1);
BigInt prod(a);
BigInt maxn;
for(int i=1;i<=n;i++)
{
maxn=max(maxn,prod/p[i].b);
prod=prod*p[i].a;
}
print(maxn);
return 0;
}
这篇博客探讨了一个优化问题,涉及到如何排列一队人,使得每个人根据其左右手持有的数字分配金币时,整体获得的金币数量最大化。通过分析相邻两人交换位置对总收益的影响,得出结论:最优的排队顺序是按照每个人手持数字乘积的降序排列。博客中使用了高精度计算来处理这个问题,并提供了一个C++实现的解决方案。
&spm=1001.2101.3001.5002&articleId=120018611&d=1&t=3&u=02c33aa70e6948a4a1dd39905d552221)
217

被折叠的 条评论
为什么被折叠?



