洛谷 p1080 国王游戏
这个国王太会玩了!
首先,由于一个大臣所获得的金币数只取决于他右手的金币数,所以前面的大臣顺序并不影响后面大臣的金币数,但会影响自己手中的金币数。然后便想到了贪心。但发现这样子并不能得出最优解。然后经过一番推导,便可以得知一个大臣的左右手乘积越大,就越要放到队伍后面。
【证明】
| …(设这一段乘积为X1) | …(设这一段乘积为Y2) |
|---|---|
| L1 | R1 |
| …(设这一段乘积为X2) | …(设这一段乘积为Y2) |
| L2 | R2 |
由上面这张表格可以知道这样的情况时:
第一个人所得的金币数为X1/R1;
第二个人所得的金币数为X1×L1×X2/R2;
所以最小值为 max(X1/R1, X1×L1×X2/R2);
然后交换两个人的位置
| …(这一段乘积为X1) | …(这一段乘积为Y2) |
|---|---|
| L2 | R2 |
| …(这一段乘积为X2) | …(这一段乘积为Y2) |
| L1 | R1 |
由上面这张表格可以知道交换后的情况时:
第一个人所得的金币数为X1×L2×X2/R1;
第二个人所得的金币数为X1/R2;
所以此时最小值为max(X1×L2×X2/R1, X1/R2);
综合上面两种情况:
如果变换之前的情况要优于变换之后的情况,那么
max(X1/R1, X1×L1×X2/R2) < max(X1×L2×X2/R1, X1/R2);
而X1/R1 < X1×L2×X2/R1 恒成立;
X1×L1×X2/R2 > X1/R2;
所以上述条件成立时
必须有X1×L1×X2/R2 < X1×L2×X2/R1 恒成立;所以化简可得 L1×R1 < L2×R2恒成立。
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
int sum[20005],ans[20005],add[20005]; //全局变量数组会在程序运行时自动初始化为0
//sum 存最后答案 add存除后的商 ans存乘法后答案
struct node{
int l;
int r;
ll l_r;
} a[1005]; //数组大小一定要比给出的数据大
int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
void times(int x) //模拟乘法
{
memset(add,0,sizeof(add));
for(int i=1;i<=ans[0];i++)
{
ans[i] = ans[i]*x; //从个位开始每个数乘x
add[i+1] += ans[i]/10; //进位 用add数组来存
ans[i] %= 10; //进位后第i位的数据
}
//因为a,b小于10000所以最多是原来的数的四倍 所以最多数组下标往后移四位
for(int i=1;i<=ans[0]+4;i++)
{
ans[i]+=add[i]; //进位
if(ans[i]>=10) {
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
//判断最高位
if(ans[i]!=0) {
ans[0]=max(ans[0],i);
}
}
return ;
}
void divition(int x) //模拟除法
{
memset(add,0,sizeof(add));
int q =0; //设q为高位余数
for(int i=ans[0]; i>=1 ;i--) //从高位开始除
{
q*=10;
q+=ans[i]; //余数乘10加下一位数
add[i] = q/x; //商
if(add[0]==0&&add[i]!=0) //记录第一次商不为0的位置
{
add[0] = i;
}
q %= x; //余数
}
}
bool compare() //比较数组存的数据的大小
{
if(sum[0]==add[0]) //如果位数一样比数据
{
for(int i=add[0]; i>=1; i--)
{
if(add[i]>sum[i]) return 1;
if(add[i]<sum[i]) return 0;
}
}
if(sum[0]<add[0]) return 1; //位数大的数据大
if(sum[0]>add[0]) return 0;
}
void cp() //将大的数组赋值给sum数组
{
memset(sum,0,sizeof(sum));
for(int i=0;i<=add[0];i++)
{
sum[i] = add[i];
}
return ;
}
bool cmp(node a,node b) //重写cmp函数 使函数从小到大排序
{
return a.l_r<b.l_r;
}
int main()
{
int n=read();
for(int i=0;i<=n;i++)
{
a[i].l = read();
a[i].r = read();
a[i].l_r =a[i].l*a[i].r;
}
sort(a+1,a+n+1,cmp); //通过自定义的cmp函数 对数组a从a[1]开始排序到a[n] 第一个是皇帝不能移动
ans[0] = 1,ans[1] = 1; //ans[0] 用来存数组里最后一位数的下标 ans[1] 表示乘数
for(int i=1;i<=n;i++)
{
times(a[i-1].l); //乘n位数
divition(a[i].r); //除右手的数
if(compare()) //找出最大的数
{
cp();
}
}
for(int i=sum[0];i>=1;i--)
printf("%d",sum[i]);
return 0;
}
諦めたらそこで試合終了ですよ。

浙公网安备 33010602011771号