2018年全国多校算法寒假训练营练习比赛(第二场)题解

时间:2023-02-13 19:08:49

A:栈模拟or字符串处理


题目链接:吐泡泡


思路:栈模拟,或者用string自带的函数进行处理,用string处理的时候要注意顺序从左到右!!!

字符串处理:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string str;
    while (cin >> str)
    {
        while (str.find("oo") != string::npos || str.find("OO") != string::npos)
        {
            if (str.find("oo") < str.find("OO")) 
                str.replace(str.find("oo"), 2, "O");
            else if(str.find("oo") > str.find("OO")) 
                str.replace(str.find("OO"), 2, "");
        }
        cout << str << endl;
    }
    return 0;
}
/*
ooOOoooO
oOOo
*/

B:01背包变形


题目链接:TaoTao要吃鸡


思路:01背包的变形。

坑点:数组要开大,开105会wa。

错误想法一:我们有可能想到,直接留1的空间装威力大的,剩下的空间进行01背包计算。但是这样是错误的,因为威力最大的可能花费很少的空间就可以装进去,比如0,那么这个时候我们的想法就不对了。那么装最重的物品呢,其实道理是一样的,如果最重的物品威力为0,我们就白白浪费这个1的空间了。

错误想法二:我们能不能,m+h-1跑01背包,并记录下来最优解我背包都装了哪些东西。但是这个想法一想就可以推翻,因为背包问题记录路径并不能实现,也可能是我太菜不会。

正确做法:于是,我们想到能不能把所有的物品都统一进行处理,但是我们给一次机会可以用1的空间拿一件任意重的物品,就可以了。

那么怎么表示这一次机会呢,dp多开一维大小为2数组就行了,0代表没用过这次机会,1代表用过这次机会了。很容易就能得出这个想法的状态转移方程了:


2018年全国多校算法寒假训练营练习比赛(第二场)题解


动态规划学的不是很好,不太会写状态转移方程,大家见谅,详情看代码吧。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n, m, h, dp[2][MAXN];
struct Node
{
    int w, v;//重量,价值
}a[MAXN];
int main()
{
    while (~scanf("%d", &n) && n)
    {
        scanf("%d%d", &m, &h);
        for (int i = 0; i < n; i++) scanf("%d%d", &a[i].w, &a[i].v);
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i++)
        {
            for (int j = m + h; j >= 1; j--)
            {
                
                if (j >= a[i].w)
                {
                    dp[0][j] = max(dp[0][j], dp[0][j - a[i].w] + a[i].v);
                    dp[1][j] = max(max(dp[1][j], dp[1][j - a[i].w] + a[i].v), dp[0][j - 1] + a[i].v);
                }
                else
                {
                    dp[1][j] = max(dp[1][j], dp[0][j - 1] + a[i].v);
                }
            }
        }
        if(h > 0) printf("%d\n", dp[1][m + h]);
        else printf("%d\n", dp[0][m]);
    }
    return 0;
}
/*
3 3 3
2 3
3 2
2 3
*/

D:LIS变形(单调非递减子序列)


题目链接:YB要打炉石


思路:单调递增子序列的变形。然而基本是模板,只需要添加一个等号。如果是N*logN的写法,需要把lower_bound换成upper_bound。

坑点:当时比赛的时候,多组输入输出会wa,现在没事了。后来他们修改了一下,然后重判了。当时wa的怀疑人生。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
int n, a[MAXN], dp[MAXN];
int main()
{
    scanf("%d", &n);
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < n; i++) scanf("%d", &a[i]);
        dp[0] = a[0];
        int len = 1;
        for(int i = 1; i < n; i++)
        {
            int pos = upper_bound(dp, dp + len, a[i]) - dp;
            if(pos < len) dp[pos] = a[i];
            else dp[len++] = a[i];
        }
        if (len < 30) printf("no\n");
        else printf("yes\n");
    }
    return 0;
}
G:DP,(打表水过)


题目链接:送分了QAQ


思路:应该是数位dp,开始没写,但是见好多人都过了,于是打表水一发,结果直接AC。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int n, m, sum[MAXN];
int main()
{
    sum[0] = 0;
    for (int i = 1; i < MAXN; i++)
    {
        sum[i] = sum[i - 1];
        string t = to_string(i);
        if (t.find("38") != string::npos || t.find("4") != string::npos) sum[i]++;
    }
    while (~scanf("%d%d", &n, &m) && (n + m))
    {
        printf("%d\n", sum[m] - sum[n - 1]);
    }
    return 0;
}
/*
1 100
0 1000000
0 0
*/

H:斐波那契数列变形


题目链接:了断局


思路:斐波那契数列的变形。从第四项开始,当前项等于前三项之和想加。记得开long long

#include<bits/stdc++.h>
using namespace std;
long long f[55];
int main()
{
    f[1] = 0; f[2] = 1; f[3] = 1;
    for (int i = 4; i < 55; i++)
    {
        f[i] = f[i - 1] + f[i - 2] + f[i - 3];
    }
    int n;
    while (~scanf("%d", &n))
    {
        cout << f[n] << endl;
    }
    return 0;
}