牛客网OJ题解--20210303

[HAOI2012]音量调节

https://ac.nowcoder.com/acm/problem/19990

本系列记录翀翀😐痛苦的刷题日志,所有题目均来自于牛客网OJ题目,坚持刷题谈起来容易做起来难,希望我可以坚持下去,这里仍然分享一段励志文案:每个人都有梦想,然而有些人把梦想变成了现实,有些人的梦想依旧是梦想,只因为他们为梦想付出的努力程度不一样,他们坚持的时间不一样,最终才有这样的结果。

NC19990-[HAOI2012]音量调节

题目链接

https://ac.nowcoder.com/acm/problem/19990

题目描述

一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量。在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。音量用一个整数描述。输入文件中给定整数beginLevel,代表吉他刚开始的音量,以及整数maxLevel,代表吉他的最大音量。音量不能小于0也不能大于maxLevel。输入文件中还给定了n个整数c1,c2,c3…..cn,表示在第i首歌开始之前吉他手想要改变的音量是多少。吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。

第一行依次为三个整数:n, beginLevel, maxlevel。第二行依次为n个整数:c1,c2,c3…..cn。输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于0或者高于maxLevel,输出-1。

测试样例

输入

3 5 10               
5 3 7

输出

10

解题思路

我们知道第i+1演出时能够达到的音量是由第i次演出后所结束的音量决定。因此我们可以定义volum[i][j]数组表示第i次表演后能否到达的音量j,0表示不能,1表示可以,那么很明显就有如下递推式

也就是说如果第i次演奏后想要达到音量j+x,那么j+x必须小于最大音量并且第i-1次演奏后可以到达音量j,第i次演奏后想要达到音量j-x,那么j-x必须大于最小音量并且第i-1次演奏后可以到达音量j。所以很明显是一个二维dp,实际上我们发现dp大部分都是可以参考0/1背包dp思路的,全部都是打表思想并且永远是后者由前者决定,只不过每一次的递推条件不同,这个具体的变式思路只能够通过多练来积累。

解题代码

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

//volum[i][j]表示的是在i此表演后能否到达音量j
//如果为1表示可以到达
//0表示无法到达
int volum[105][1005];

int main()
{
    int n, b, m;
    cin >> n >> b >> m;
    //未开始演出时可以到达初始音量,所以置为1
    volum[0][b] = 1;
    for (int i = 1; i <= n; i++)
    {
        //对于每一次的音量调节大小x
        int x;
        cin >> x;
        //注意枚举表演i首后能否到达音量j
        //因为还是二维0/1dp没有降维,所以j正序逆序都可以
        for (int j = 0; j <= m; j++)
        {
            //当且仅当j+x没有超出最大音量,且在表演i-1次后音量可以到达j时
            //表演第i首歌以后才可以到达j+x音量
            if (j + x <= m && volum[i - 1][j])
                volum[i][j + x] = 1;
            //当且仅当jxx没有超出最小音量,且在表演i-1次后音量可以到达j时
            //表演第i首歌以后才可以到达j-x音量
            if (j - x >= 0 && volum[i - 1][j])
                volum[i][j - x] = 1;
        }
    }
    //但是注意最终要求的是寻找表演n次后可以到达的最大音量
    //所以此时i必须为逆序的
    for (int i = m; i >= 0; i--)
    {
        //一定是volum[n][i]因为必须表演完n次
        if (volum[n][i])
        {
            //第一个出现的1值就是可以到达的最大音量
            //输出这个最大音量i即可
            cout << i << endl;
            system("pause");
            return 0;
        }
    }
    //如果整个第n行都是0,说明无法表演完n首歌,所以输出-1
    cout << "-1" << endl;
    system("pause");
    return 0;
}

NC21467-货币系统

题目链接

https://ac.nowcoder.com/acm/problem/21467

题目描述

在网友的国度***有n种不同面额的货币,第i种货币的面额为a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为n、面额数组为a[1..n]的货币系统记作(n,a)。在一个完善的货币系统中,每一个非负整数的金额x 都应该可以被表示出,即对每一个非负整数x,都存在n个非负整数t[i] 满足a[i] x t[i] 的和为x。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额x不能被该货币系统表示出。例如在货币系统n=3, a=[2,5,9]中,金额1,3就无法被表示出来。两个货币系统(n,a)和(m,b)是等价的,当且仅当对于任意非负整数x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。现在网友们打算简化一下货币系统。他们希望找到一个货币系统(m,b),满足(m,b) 与原来的货币系统(n,a)等价,且m尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的m。输入的第一行包含一个整数T,表示数据组数。接下来按照如下格式分别给出T组数据。每组数据的第一行包含一个正整数n。接下来一行包含n个由空格隔开的正整数a[i]。输出文件共T行, 对于每组数据, 输出一行一个正整数, 表示所有与(n, a)等价的货币系统(m, b)中, 最小的m。

测试样例

输入

2
4
3 19 10 6
5
11 29 13 19 17

输出

2
5

说明

在第一组数据中,货币系统(2, [3,10])和给出的货币系统(n, a)等价,并可以验证不存在m < 2的等价的货币系统,因此答案为2。
在第二组数据中,可以验证不存在m < n的等价的货币系统,因此答案为5。

解题思路

首先我们很容易想到货币系统之所以可以减少货币数量,是因为给出的某些货币可以由更小的货币组成,比如样例1中的19可以由货币10+3+6组成,所以我们可以得到一个关系,价值大的货币是否可有可无主要取决于能否被小货币组合表示。因此我们定义vis数组,0表示不能被其他小货币表示,1表示可以被其他小货币表示即可有可无。那么我们将货币按价格有小到大排序,对于每一个货币k,如果他可以被表示,那么对于由k和其他货币a[i]组成的货币价格k+a[i]肯定也是可以被表示的。

解题代码

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

const int N = 1e5;
//存储货币
int a[105];
//vis标记数组
//0表示不可以被其他货币表示
//1表示可以被其他货币表示也就可有可无
int vis[N];

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        //初始化
        memset(a, 0, sizeof(a));
        memset(vis, 0, sizeof(vis));
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
        }
        //排序
        sort(a + 1, a + n + 1);
        //默认每一个货币都是必须存在的
        int ans = n;
        //首先0肯定是可有可无的
        vis[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            //如果vis为1,那么这个货币可有可无
            //货币数量-1
            if (vis[a[i]])
            {
                ans--;
            }
            //否则这个货币必须存在
            //然后所有可以表示的货币值j+a[i]肯定也就是可以被表示了
            //都标记为1表示可有可无
            for (int j = 0; j <= a[n]; j++)
            {
                //枚举j从a[i]到最大货币值
                //如果j是可以被表示的
                if (vis[j])
                {
                    //那么j+a[i]肯定也可以被表示
                    vis[j + a[i]] = 1;
                }
            }
        }
        cout << ans << endl;
    }
    system("pause");
    return 0;
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务