本博客已停止更新!请移步至louhc.github.io

「洛谷P1080」「NOIP2012提高组」国王游戏 解题报告

P1080 国王游戏

题目描述

恰逢 \(H\)国国庆,国王邀请\(n\)位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 \(n\) 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。

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

输入输出格式

输入格式:

第一行包含一个整数\(n\),表示大臣的人数。

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

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

输出格式:

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

输入输出样例

输入样例#1:

3 
1 1 
2 3 
7 4 
4 6 

输出样例#1:

2

说明

【输入输出样例说明】

按11、22、33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;

按 11、33、22 这样排列队伍,获得奖赏最多的大臣所获得金币数为22;

按 22、11、33 这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;

按22、33、11这样排列队伍,获得奖赏最多的大臣所获得金币数为99;

按 33、11、22这样排列队伍,获得奖赏最多的大臣所获得金币数为 22;

按33、22、11 这样排列队伍,获得奖赏最多的大臣所获得金币数为 99。

因此,奖赏最多的大臣最少获得 22个金币,答案输出 22。

【数据范围】

对于 20%的数据,有 1≤ n≤ 10,0 < a,b < 81≤n≤10,0<a,b<8;

对于 40%的数据,有1≤ n≤20,0 < a,b < 81≤n≤20,0<a,b<8;

对于 60%的数据,有 1≤ n≤1001≤n≤100;

对于 60%的数据,保证答案不超过 10^9109;

对于 100%的数据,有 1 ≤ n ≤1,000,0 < a,b < 100001≤n≤1,000,0<a,b<10000。

NOIP 2012 提高组 第一天 第二题


思路

我们假设一个正确的序列中有两个元素 i 与 i + 1,他们左右手的数字分别为Ai,Bi,Ai+1,Bi+1

排在i大臣前面的所有人的左手上的数的乘积为T(以下向下取整省略不写)

这两位大臣得到最多的硬币为 max( T / Bi, (T * Ai) / Bi+1 ) = Ans1

假设交换这两位大臣 则他们得到最多硬币数变为 max( T / Bi+1, (T * Ai+1) / Bi ) = Ans2

要求Ans1 <= Ans2,原序列顺序可能不是最优(否则这两位大臣交换能得到更有策略)

所以要使 T / Bi 和 (T * Ai) / Bi+1都小于等于Ans2即可满足要求

由于T / Bi <= (T * Ai+1) / Bi ,T / Bi 肯定能满足要求,只要使 ( T * Ai ) / Bi+1 也小于等于 Ans2即可

(T * Ai) / Bi+1 肯定大于等于 T / Bi+1 ,所以只能使 (T * Ai+1) / Bi 大于等于(T * Ai) / Bi+1 ,否则不能达到要求

然后我们就可以列出不等式 (T * Ai) / Bi+1 <= (T * Ai+1) / Bi

因为T是正数,所以可以同时消去

Ai / Bi+1 <= Ai+1 / Bi

同乘Bi+1Bi

Ai * Bi <= Ai+1 * Bi+1

所以,只要按Ai * Bi从小到大排序就不成问题了。(当然别把国王也排序了)

注意要加高精度。

代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 10005

struct ppp{
    int a, b;
}p[MAXN];

int n;

bool cmp( ppp x, ppp y ){
    return x.a * x.b < y.a * y.b;
}

int s[4005], len;
int ans[4005], ans_l(0);

void Mul( int x ){
    int t(len);
    for ( int i = 1; i <= len; ++i ) s[i] *= x;
    for ( int i = 1; i <= len + 5; ++i ){
        if ( s[i] ) t = i;
        s[i + 1] += s[i] / 10;
        s[i] %= 10;
    }
    len = t;
}

void Copy( int c[], int c_l ){
    for ( int i = 1; i <= c_l; ++i ) ans[i] = c[i];
    ans_l = c_l;
}

void Out( int s[], int l ){
    for ( int i = l; i >= 1; --i ) printf( "%d", s[i] );
    putchar('\n');
}
void Div( int x ){
    int c[4005], l(-1);
    for ( int i = 1; i <= len; ++i ) c[i] = s[i];
    for ( int i = len; i >= 1; --i ){
        c[i - 1] += ( c[i] % x ) * 10;
        c[i] /= x;
        if ( c[i] && l == -1 ) l = i;
    }
    if ( ans_l == l ){
        bool flg(0);
        for ( int i = l; i >= 1; --i )
            if ( ans[i] < c[i] ){ flg = 1; break; }
            else break;
        if ( flg ) Copy( c, l );
    }
    if ( ans_l < l ) Copy( c, l );
}


int main(){
    scanf( "%d", &n );
    scanf( "%d%d", &p[0].a, &p[0].b );
    for ( int i = 1; i <= n; ++i ) scanf( "%d%d", &p[i].a, &p[i].b );
    sort( p + 1, p + n + 1, cmp );
    s[1] = 1; len = 1;
    Mul( p[0].a );
    for ( int i = 1; i <= n; ++i )
        Div( p[i].b ), Mul( p[i].a );
    Out( ans, ans_l );
    return 0;
}

完事!!!

posted @ 2018-11-26 21:36  louhc  阅读(518)  评论(0编辑  收藏  举报