洛谷 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; 
    
 } 

 

  参考链接:https://www.luogu.org/blog/635forever/guo-wang-you-hu

posted @ 2019-11-19 11:26  七里、  阅读(291)  评论(1)    收藏  举报