牛客网小白月赛34题解

A dd爱科学1.0
题意:

给出一个字符串,问最少修改多少个字母使得该字符串为非递减字符串?

题解:

动态规划。
原问题的子问题:
最少修改多少个字母使得前i个字符形成的字符串为非递减序列,且保证最后一个字符为j。
定义:
dp[i][j] 保证前i个字符串为非递减且最后一个字符为j的最少修改次数
转移方程:

  • 当s[i] != j时即第i个字符不是j时,需要将s[i]个改成字符j再加上min(dp[i-1][k])。min(dp[i-1][k])表示前i-1个字符形成非递减且最大字符小于等于j的最少修改次数。
    dp[i][j] = min(dp[i-1][k]) +1 其中k<=j (保证非递减)
  • 当s[i] == j时即第i个字符恰好是j时。不需要修改s[i],min(dp[i-1][k])解释同上。
    dp[i][j] = min(dp[i-1][k]) 其中k<=j (保证非递减)

最终答案为min(dp[n-1][k]) (0<=k<=25)

维护:
min(dp[i-1][k]) ,可以用二维数组mi[i-1][x]维护,mi[i-1][x]表示,前i-1个字符中最大字符为k的最小修改次数。由于每次使用min(dp[i-1][k])时都是i与i-1的关系,所以可以省略掉i-1这一维度,即可以使用一维数组mi[x]进行维护。
mi[x] = min(mi[x-1],dp[i-1][x]) (0<=x<=25,大写字母的范围)很简单的维护思路。可以类比为:给出一个数组,求出前x个元素中的最小值。

代码:
#include <bits/stdc++.h>
using namespace std;
int n;
const int maxn = 1e6+5;
int dp[maxn][27],mi[27];
int main()
{
    cin>>n;
    string s;
    cin>>s;
    memset(dp,0x3f,sizeof(dp));//初始化为0x3f3f3f3f 1e9左右
    //初始化dp[i][j]
    for(int j=0;j<26;++j)
        dp[0][j]=1;
    dp[0][int(s[0]-'A')]=0;
    //初始化mi[x]
    mi[0]=dp[0][0];
    for(int i=1;i<26;++i)
    {
        mi[i]=min(mi[i-1],dp[0][i]);
    }
    for(int i=1;i<n;++i)
    {
        int x =int(s[i]-'A');
        //状态转移
        dp[i][x]=min(dp[i][x],mi[x]);
        for(int j=0;j<26;++j)
        {
            if(j==x)continue;
            dp[i][j]=min(dp[i][j],mi[j]+1);
        }
        //维护mi[x]
        mi[0]=dp[i][0];
        for(int j=1;j<26;++j)
        {
            mi[j]=min(mi[j-1],dp[i][j]);
        }
    }
    int ans = (1<<30);
    //取最小值
    for(int i=0;i<26;++i)
    {
        ans = min(ans,dp[n-1][i]);
    }
    cout<<ans;
    return 0;
}
E dd爱旋转
题意:

给出一个n*n二维矩阵,有两种操作:

  1. 顺时针旋转180度
  2. 水平镜像,以中间行为轴水平镜像

给出q个操作,问所有操作完成后,二维矩阵转换成什么了?

题解:

手动模拟一下。
操作1:相当于是a[i][j] = a[n-i+1][n-j+1]
操作2:相当于是a[i][j] = a[n-i+1][j]

再模拟一下,发现结果与操作顺序无关,且操作1与操作2,再进行偶数次时与原矩阵相比不会产生变化。
所以结果就是看操作1与操作2是否执行了奇数次。然后模拟即可。

代码:
#include <bits/stdc++.h>
using namespace std;
int n,q;
int a[1005][1005];
int main()
{
    cin>>n;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<n;++j)
        {
            cin>>a[i][j];
        }
    }
    cin>>q;
    int dj = 0,px = 0;
    while(q--)
    {
        int op ;
        cin>>op;
        if(op==1)
            dj++;
        else
            px++;
    }
    dj%=2;
    px%=2;
    if(dj==1&&px==1)
    {
        for(int i=0;i<n;++i)
        {
            for(int j=n-1;j>=0;--j)
            {
                cout<<a[i][j]<<" ";
            }
            cout<<"\n";
        }    
    }
    else if(dj==1)
    {
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<n;++j)
            {
                cout<<a[n-1-i][n-1-j]<<" ";
            }
            cout<<"\n";
        }    
    }
    else if(px==1)
    {
        for(int i=n-1;i>=0;--i)
        {
            for(int j=0;j<n;++j)
            {
                cout<<a[i][j]<<" ";
            }
            cout<<"\n";
        }    
    }
    else
    {
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<n;++j)
            {
                cout<<a[i][j]<<" ";
            }
            cout<<"\n";
        }   
    }
    return 0;
}
F dd爱框框
题意:

给出一个长度为n的数组,和目标x。
问子区间[l,r]的元素和大于等于x的最小子区间是多少?
最小子区间的定义:区间长度最小,区间长度相同时,左边界最小。

题解:

滑动窗口。
定义 l ,r,sum,分别为区间左右编辑,以及区间和。
r不断地向右移动,过程中维护sum。
过程中若是sum-a[l]>=x的话,证明在满足题意得情况下,区间可以更小,则不断增大左边界l,直至sum-a[l]<x。
当sum>=x时,注意维护最小区间。

代码:
#include <bits/stdc++.h>
using namespace std;
int n,x;
int a[10000005];
int main()
{
    cin>>n>>x;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int anslen=x,ansl=1;
    int l=1,r=1,sum=0;
    while(r<n)
    {
        sum+=a[r++];
        while(sum-a[l]>=x)
        {
            sum-=a[l++];
        }
        if(sum>=x&&r-l<anslen)
        {
            anslen=r-l;
            ansl=l;
        }
    }
    cout<<ansl<<" "<<ansl+anslen-1;
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值