NOIP2012 Day1 国王游戏

【问题描述】

n+1个人排成一列,从2到n+1个人所获得的金币为前面所有人左手数字之和除以自己右手的数字。并对这n个人进行排列使得获得金币最多的人获得的金币尽可能的少。

【格式】

输入:
第一行,输入n
接下来n+1行,每行两个数a,b,表示第i个人左手和右手的数字
输出:
获得的最少金币数

【输入样例】

3
1 1
2 3
7 4
4 6

【输出样例】

2

【说明】

【输入输出样例说明】
按 11 、 22 、 33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22 ;
按 11 、 33 、 22 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22 ;
按 22 、 11 、 33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22 ;
按 22 、 33 、 1 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 99 ;
按 33 、 11 、 2 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22 ;
按 33 、 22 、 11 这样排列队伍,获得奖赏最多的大臣所获得金币数为 99 。
因此,奖赏最多的大臣最少获得 2 2 个金币,答案输出 22 。
【数据范围】
对于 20%的数据,有 1≤ n≤ 10,0 < a,b < 8,1≤n≤10,0 < a,b<8 ;
对于 40%的数据,有 1≤ n≤20,0 < a,b < 81≤n≤20,0 < a,b<8 ;
对于 60%的数据,有 1≤ n≤100,1≤n≤100;
对于 60%的数据,保证答案不超过 10^9;
对于 100%的数据,有 1 ≤ n ≤1,000,0 < a,b < 10000,1≤n≤1,000,0 < a,b < 10000


【分析】

此处需要用到一些数学证明,先上结论最小的排列,是根据a*b降序排列后的数列。
证明如下

现在考虑相邻的两个人,设他们前面所有人的左手的乘积为k。
交换两人的位置会有两种不同的大小:
方案一:max{k/b1,k*a1/b2}
方案二:max{k/b2.k*a2/b1}
不妨设方案一 < 方案二
又因为k/b2 < k*a1/b2 ,k/b1 < k*a2/b1
所以k*a1/b2 < k*a2/b1
整理可得a1*b1 < a2*b2
(即当a1*b1>=a2*b2时,两人需要交换位置)
利用冒泡排序的思想,可以得到当ai*bi成升序排列时,最大金币数最小。

然后就只是高精乘低精和高精除低精。
下面附上代码一份:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int N=1001;
struct node
{
    int a,b,mul;
}num[maxn];
int n;
bool cmp(node x,node y)
{
    return x.mul<y.mul;
}
int ret[N],ans[N];
int tmp[N],lt;
int lr,la;
bool check()
{
    if(lt>la) return 1;
    if(lt<la) return 0;
    for(int i=lt;i>=1;i--)
    {
        if(tmp[i]>ans[i]) return 1;
        if(tmp[i]<ans[i]) return 0;
    }
    return 0;
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
    {
        scanf("%d%d",&num[i].a,&num[i].b);
        num[i].mul=num[i].a*num[i].b;//提取关键字
    }
    sort(num+1,num+n+1,cmp);//关键字排序
    ret[1]=num[0].a,lr=1,la=0;
    for(int i=1;i<=n;i++)
    {//高精乘除,ret累计乘,ans储存最佳答案,tmp计算第i个人(除去第一个人)所获得金币数
        int a=num[i].a,b=num[i].b;
        lt=lr;
        for(int j=1;j<=lt;j++)
            tmp[j]=ret[j];
        for(int j=lt;j>=1;j--)
            tmp[j-1]+=tmp[j]%b*10000,tmp[j]=tmp[j]/b;
        while(tmp[lt]<=0) lt--;
        if(check())
        {
            la=lt;
            for(int j=1;j<=la;j++)
                ans[j]=tmp[j];
        }
        for(int j=lr;j>=1;j--)
            ret[j]*=a;
        for(int j=1;j<=lr;j++)
            ret[j+1]+=ret[j]/10000,ret[j]%=10000;
        while(ret[lr+1]>0)
        {
            lr++;
            ret[lr+1]+=ret[lr]/10000;
            ret[lr]=ret[lr]%10000;
        }
    }
    printf("%d",ans[la]);
    for(int i=la-1;i>=1;i--)
        printf("%04d",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80931415