洛谷Oj-P1080 國王遊戲-貪心+高精度

題目描述:
恰逢 H 國國慶,國王邀請 n 位大臣來玩一個有獎遊戲。首先,他讓每個大臣在左、右手上面分別寫下一個整數,國王自己也在左、右手上各寫一個整數。然後,讓這 n 位大臣排成一排,國王站在隊伍的最前面。排好隊後,所有的大臣都會獲得國王獎賞的若干金幣,每位大臣獲得的金幣數分別是:排在該大臣前面的所有人的左手上的數的乘積除以他自己右手上的數,然後向下取整得到的結果。
國王不希望某一個大臣獲得特別多的獎賞,所以他想請你幫他重新安排一下隊伍的順序,使得獲得獎賞最多的大臣,所獲獎賞儘可能的少。注意,國王的位置始終在隊伍的最前面。

AC代碼:

struct A
{
    int l;
    int r;
};
A a[1010];
//排在該大臣前面的所有人的左手上的數的乘積(以下簡稱乘積)
int m[5000],d[5000],max_coin[5000];//數組d爲每位大臣所獲得的金幣數,數組max_coin爲答案
bool cmp(const A &a,const A &b)//比較函數
{
    return a.l * a.r < b.l * b.r;
}
bool compare(int a[],int b[])//判斷a是否 >= b
{
    if(a[0] > b[0])//先比較位數
        return true;
    if(a[0] < b[0])//先比較位數
        return false;
    for(int i = a[0]; i >= 1; --i)//若位數相同,則從最高位開始比較每一位的大小
    {
        if(a[i] > b[i])
            return true;
        if(a[i] < b[i])
            return false;
    }
    return true;
}
void print(int a[])//a[1]爲最低位,a[a[0]]爲最高位,所以要逆序輸出
{
    for(int i = a[0]; i >= 1; --i)
        cout << a[i];
    cout << endl;
}
void multiply(int n)//低精度數n乘高精度數m
{
    for(int i = 1; i <= m[0]; ++i)//m的每一位都乘n
        m[i] = m[i] * n;
    for(int i = 1; i <= m[0]; ++i)//處理進位
    {
        m[i + 1] += m[i] / 10;
        m[i] %= 10;
    }
    m[0]++;
    while(m[m[0]] >= 10)//處理最高位的進位
    {
        m[m[0] + 1] += m[m[0]] / 10;
        m[m[0]] %= 10;
        m[0]++;
    }
    if(m[m[0]] == 0)//特判
        m[0]--;
    return;
}
void divide(int n)//低精度數n除高精度數m,結果保存在高精度數d中
{
    memset(d,0,sizeof(d));//初始化
    int size = m[0];
    int t = 0;
    int flag = 1;
    for(int i = size; i >= 1; --i)//從高位向低位
    {
        d[i] = (m[i] + 10 * t) / n;//商
        t = (m[i] + 10 * t) % n;//餘數項,迭代
        if(d[i] != 0 && flag == 1)//確定商d的位數,遇到第一個不爲0的位,並且一次性變量flag的值爲1
        {
            d[0] = i;//則商有i位
            flag = 0;//變量作廢
        }
    }
    return;
}
int main()
{
    int n;
    cin >> n;//輸入大臣的人數
    for(int i = 0; i <= n; ++i)//輸入國王+大臣的左右手
        cin >> a[i].l >> a[i].r;
    sort(a + 1, a + n + 1,cmp);//排序以進行貪心
    //初始化乘積
    m[0] = 1;
    m[1] = 1;
    multiply(a[0].l);//乘上國王的左手上的數
    for(int i = 1; i <= n; ++i)//對於每一位大臣
    {
        divide(a[i].r);//除以自己右手上的數,答案放在數組d中
        multiply(a[i].l);//乘以自己左手上的數
        if(compare(d,max_coin) == true)//如果所獲得的金幣更多,就更新答案
            memcpy(max_coin,d,sizeof(d));//直接copy過去!
    }
    print(max_coin);
    return 0;
}

解決方法:
因爲這道題需要做乘積而且最極端的情況是100001000,即104000,需要寫高精度。
題目要使獲得獎賞最多的大臣,所獲獎賞儘可能少
再看題目的問法,有點像二分答案
看了題解才知道是貪心。
爲了簡化證明,不妨假設只有兩名大臣

人物 左手 右手
國王 a0 b0
大臣① a1 b1
大臣② a2 b2

此時大臣所獲得的獎賞的最大值 ans1=max(a0b1,a0a1b2)ans1= max(\frac{a0}{b1},\frac{a0 * a1}{b2})
另一種情況爲

人物 左手 右手
國王 a0 b0
大臣② a2 b2
大臣① a1 b1

此時大臣所獲得的獎賞的最大值 ans2=max(a0b2,a0a2b1)ans2 = max(\frac{a0}{b2},\frac{a0 * a2}{b1})
我們想大臣獲得的獎賞的最大值儘可能小,就要選擇最大值較小的那種排列方法
對上式替換可以得到
ans1=max(k1,k2)ans1= max(k1,k2)
ans2=max(k3,k4)ans2= max(k3,k4)
由於
a0a2b1&gt;a0b1\frac{a0 * a2}{b1} &gt; \frac{a0}{b1}
a0a1b2&gt;a0b2\frac{a0 * a1}{b2} &gt; \frac{a0}{b2}
所以
k2 > k3
k4 > k1
如果ans1 < ans2
可以得到k4>k2(k3不如k2大,k4比k1大,只有k4比k2大才能滿足ans2>ans1)

a0a1b2&gt;a0a2b1\frac{a0 * a1}{b2} &gt; \frac{a0 * a2}{b1}
化簡得
a1b1&lt;a2b2a1 * b1 &lt; a2 * b2
於是得出結論,如果想要大臣獲得的獎賞的最大值儘可能小,就要將左手與右手的乘積較小的大臣放在隊伍的前面
確定了排列順序後,一輪模擬取最大值就可以了(此時的最大值便是所有最大值中最小的)
貪心中的排隊問題可以用技巧來做分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章