首页 > 其他分享 >洛谷题单指南-贪心-P1080 [NOIP2012 提高组] 国王游戏

洛谷题单指南-贪心-P1080 [NOIP2012 提高组] 国王游戏

时间:2024-02-27 16:24:39浏览次数:18  
标签:10 NOIP2012 int 洛谷题 back vector P1080 res size

原题链接:https://www.luogu.com.cn/problem/P1080

题意解读:通过不同的排队方式,让获得最多奖赏的大臣金额尽可能的少。此题如果没有思路,用全排列枚举可以“骗”分,更好的做法直觉上是某种贪心策略,另外基于数据规模考虑,要拿满分,需要上高精度。下面就由浅入深一步一步的解决。

解题思路:

1、枚举排列

观察数据,前20%的数据量级在10以内,如果用全排列暴力枚举,复杂度在10!*10*10以内,能得多少分就听天由命。

全排列可以借助next_permuation函数,也可以自行dfs

20分代码:

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

int n;
pair<int, int> a[1005];
long long ans = LLONG_MAX;

int main()
{
    cin >> n;
    for(int i = 0; i <= n; i++) cin >> a[i].first >> a[i].second;

    vector<int> v;
    for(int i = 1; i <= n; i++) v.push_back(i); //初始排列
   
    do 
    {
        long long t = a[0].first; //初始乘积是国王左手数字
        long long maxx = 0;
        for(auto i : v)
        {
            maxx = max(maxx, t / a[i].second); //计算每一个人获得的奖励
            t = t * a[i].first; //乘积乘上当前左手的数,便于计算下一个人的奖励
        }
        ans = min(ans, maxx);

    } while(next_permutation(v.begin(), v.end()));

    cout << ans;

    return 0;
}

2、贪心策略-1

由于要得到获得最大金币数的人,

而对于每一个人,要想获得最大金币数,必须排在最后一个位置

证明:对于一个人左手a、右手b,排在前面的所有人左手乘积是t,其获得的金币数为t / b

要使得金币数越大,t自然是越大越好,排名越靠后,t越大

因此,对于每一个人, 其能获得的最大金币数为 T / (a * b),T表示所有人左手数字乘积

那么只需要找到能获得的最大金币数中最小值即可

50分代码:

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

int n;
pair<int, int> a[1005];
long long ans = LLONG_MAX;
long long t = 1;

int main()
{
    cin >> n;
    for(int i = 0; i <= n; i++)
    {
        cin >> a[i].first >> a[i].second;
        t *= a[i].first;
    } 

    for(int i = 1; i <= n; i++)
    {
        long long res = t / (a[i].first * a[i].second);
        if(res > 0) ans = min(ans, res); //res>0是因为如果大臣最多获得金币是0,不参与比较
    }

    cout << ans;
    
    return 0;
}

接下来的问题,就是要把计算乘法、除法的地方替换成高精度,主要会用到高精度*低精度,高精度➗低精度

在“模拟与高精度”模块中已多次介绍,不再具体讲述其实现原理。

90分代码:

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

int n;
pair<int, int> a[1005];
vector<int> ans(4005, 9); //1≤n≤1,000,0<a,b<10000, 所有a乘积长度在4000左右,定义一个最大值
vector<int> t(1, 1); //所有左手数的乘积

vector<int> mul(vector<int> &a, int b)
{
    vector<int> res;

    int t = 0;
    for(int i = 0; i < a.size(); i++)
    {
        t += a[i] * b;
        res.push_back(t % 10);
        t /= 10;
    }

    while(t)
    {
        res.push_back(t % 10);
        t /= 10;
    }

    return res;
}

vector<int> div(vector<int> &a, int b)
{
    vector<int> res; 
    int r = 0;
    for(int i = a.size() - 1; i >= 0; i--)
    {
        r = 10 * r + a[i];
        if(r >= b)
        {
            res.push_back(r / b);
            r %= b;
        }
        else res.push_back(0);
    }

    //将res翻转过来,统一低位在前的方式
    vector<int> res2; 
    for(int i = res.size() - 1; i >= 0; i--)
    {
        res2.push_back(res[i]);
    }

    //去除前导0
    while(res2.back() == 0 && res2.size() > 1) res2.pop_back();

    return res2;
}

//a > b返回正数,a = b返回0, a < b返回负数
int compare(vector<int> &a, vector<int> &b)
{
    if(a.size() != b.size()) return a.size() - b.size();
    else
    {
        for(int i = a.size() - 1; i >= 0; i--)
        {
            if(a[i] != b[i]) return a[i] - b[i];
        }
        return 0;
    }   
}

int main()
{
    cin >> n;
    for(int i = 0; i <= n; i++)
    {
        cin >> a[i].first >> a[i].second;
        t = mul(t, a[i].first);
    } 

    for(int i = 1; i <= n; i++)
    {
        vector<int> res = div(t, a[i].first * a[i].second);
        if(compare(ans, res) > 0) ans = res;
    }

    for(int i = ans.size() - 1; i >= 0; i--) cout << ans[i];
    
    return 0;
}

3、贪心策略-2

以上方法为什么只能得到90分?

原因在于此方法无法处理左手数全是1的情况,这样每个人排最后不一定代表这种排序方式的最大值,因为有可能右手数大于1,这样获得金币为0

前面如果有一个右手数是1的,获得金币为1,这才是最大值。

举例:

2
1 100
1 1
1 2 

在以上策略下输出是0,真实结果应该是1。

换一种思路,设第i、j两个人,i、j连续,i左手a[i].l、右手a[i].r,j左手a[j].l、右手a[j].r

如果i在j前面,对最终答案的影响值是a[i].l / a[j].r

如果j在i前面,对最终答案的影响值是a[j].l / a[i].r

要保持i在j前面,必须a[i].l / a[j].r < a[j].l / a[i].r

有a[i].l * a[i].r < a[j].l * a[j].r

因此得知,一个人排在前面,必须左右手乘积更小,这样答案才会更小。

所以,只需要对每个大臣按左右手乘积从小到大排序,再计算最大的金币即可。

100分代码:

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

struct p
{
    int l ,r;
} a[1005];

bool cmp(p p1, p p2)
{
    return p1.l * p1.r < p2.l * p2.r;
}

int n;
vector<int> ans(1, 0); 
vector<int> t(1, 1);

vector<int> mul(vector<int> &a, int b)
{
    vector<int> res;

    int t = 0;
    for(int i = 0; i < a.size(); i++)
    {
        t += a[i] * b;
        res.push_back(t % 10);
        t /= 10;
    }

    while(t)
    {
        res.push_back(t % 10);
        t /= 10;
    }

    return res;
}

vector<int> div(vector<int> &a, int b)
{
    vector<int> res; 
    int r = 0;
    for(int i = a.size() - 1; i >= 0; i--)
    {
        r = 10 * r + a[i];
        if(r >= b)
        {
            res.push_back(r / b);
            r %= b;
        }
        else res.push_back(0);
    }

    //将res翻转过来,统一低位在前的方式
    vector<int> res2; 
    for(int i = res.size() - 1; i >= 0; i--)
    {
        res2.push_back(res[i]);
    }

    //去除前导0
    while(res2.back() == 0 && res2.size() > 1) res2.pop_back();

    return res2;
}

//a > b返回正数,a = b返回0, a < b返回负数
int compare(vector<int> &a, vector<int> &b)
{
    if(a.size() != b.size()) return a.size() - b.size();
    else
    {
        for(int i = a.size() - 1; i >= 0; i--)
        {
            if(a[i] != b[i]) return a[i] - b[i];
        }
        return 0;
    }   
}

int main()
{
    cin >> n;
    for(int i = 0; i <= n; i++)
    {
        cin >> a[i].l >> a[i].r;
    } 

    sort(a + 1, a + n + 1, cmp);

    t = mul(t, a[0].l); //国王的左手值
    for(int i = 1; i <= n; i++)
    {
        vector<int> res = div(t, a[i].r); //计算当前人活得的金额
        if(compare(ans, res) < 0) ans = res; //更新最大值
        t = mul(t, a[i].l); //t乘以当前人左手数,便于计算下一个
    }

    for(int i = ans.size() - 1; i >= 0; i--) cout << ans[i];
    
    return 0;
}

 

标签:10,NOIP2012,int,洛谷题,back,vector,P1080,res,size
From: https://www.cnblogs.com/jcwy/p/18037102

相关文章

  • 洛谷题单指南-贪心-P4447 [AHOI2018初中组] 分组
    原题链接:https://www.luogu.com.cn/problem/P4447题意解读:将一个有序的数列,按不重复连续数分成一组,可分成若干组,使得人数最少的组在各种分组方式之中是最大的。解题思路:观察样例说明,有6个测试点的ai​互不相同,因此直接将数据排序,然后连续数分成一组,计算每组数量最少的,即为答案,6......
  • 洛谷题单指南-贪心-P4995 跳跳!
    原题链接:https://www.luogu.com.cn/problem/P4995题意解读:消耗最大的体力跳完所有石头,贪心选择问题。解题思路:贪心策略:每次保证有最大的高度差即可,第一次跳到最大高度然后跳到最小高度,再跳到剩下的最大高度,再跳第二小高度,以此类推,直到跳完所有石头。100分代码:#include<bi......
  • 洛谷题单指南-贪心-P1094 [NOIP2007 普及组] 纪念品分组
    原题链接:https://www.luogu.com.cn/problem/P1094题意解读:贪心选择解题思路:贪心策略:将纪念品按价格由小到大排序,优先选择价格大的一直到超过分组价格上限,再选择价格小的直到超过价格上限,此为一组重复以上过程,直到所有数据都遍历到,采用一头一尾双指针即可。证明:如果最大价格......
  • 洛谷题单指南-贪心-P1208 [USACO1.3] 混合牛奶 Mixing Milk
    原题链接:https://www.luogu.com.cn/problem/P1208题意解读:就是一个部分背包问题,贪心模版题。解题思路:优先选择单价低的牛奶即可。100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=5005;structfarmer{intprice,count;}f[N];boolcmp(fa......
  • 洛谷题单指南-贪心-P5019 [NOIP2018 提高组] 铺设道路
    原题链接:https://www.luogu.com.cn/problem/P5019题意解读:最短时间内填满道路,连在一起的不为0的坑可以一起填解题思路:方法1:分治法对于一段连续不同深度的坑,可以最多连续填的天数是最小深度在填满最小深度之后,分别针对其左边和右边的区域再进行填充,这就是此题分治法的理论基......
  • 洛谷题单指南-贪心-P1478 陶陶摘苹果(升级版)
    原题链接:https://www.luogu.com.cn/problem/P1478题意解读:题目的本质是任务安排问题,有n件任务,每件任务耗时不同,在一定的时间内,如何安排任务使得完成的任务越多越好。解题思路:对于这类问题,贪心策略是优先完成容易的。回到摘苹果问题,要优先摘耗费力气小的,如果高度够不着,就跳过,......
  • 洛谷题单指南-贪心-P1106 删数问题
    原题链接:https://www.luogu.com.cn/problem/P1106题意解读:如何删数,让剩下的数最小,贪心选择问题。解题思路:先看样例:1754384第1次遍历:删掉7,剩下15438第2次遍历:删掉5,剩下1438第3次遍历:删掉4,剩下138第4次遍历:删掉8,剩下13,即为结果所以,贪心策略如下:1、遍历每一个数,如果前一......
  • P1082 [NOIP2012 提高组] 同余方程
    原题链接扩展欧几里得算法的应用,关于原理性的讲解这里就略去了,这边给出学习链接即模板。intexgcd(inta,intb,int&x,int&y){if(b==0){x=1;y=0;returnx;}intd=exgcd(b,a%b,x,y);x=y;y=d-a/b*y;returnx;}文......
  • 洛谷题单指南-贪心-P3817 小A的糖果
    原题链接:https://www.luogu.com.cn/problem/P3817题意分析:吃最少的糖果,保证相邻糖果数之和不大于x,需要某种贪心策略。解题思路:依次遍历相邻两盒糖果如果糖果数之和大于x,必须要吃点一部分,使得糖果数之和刚好等于x贪心策略是:优先吃后一盒糖果,因为这样可以更利于后续的判断成立......
  • 洛谷题单指南-贪心-P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
    原题链接:https://www.luogu.com.cn/problem/P1090题意解读:两两合并,是典型的哈夫曼编码算法思想,贪心即可。解题思路:要是合并体力消耗最少,就要让尽可能少的果子越晚合并越好,因此,贪心策略为优先选择数量最少的两堆果子合并,一直到剩下一堆果子,把合并过程中的消耗值累加即可,要快速......