牛客周赛 Round 1【题解完成】

29 阅读4分钟
题目难度知识点
A 游游画U找规律
B 训练参赛签到
C 游游的交换字符★★思维
D 游游的9的倍数★★计数类DP

image.png C题暴力做的,正解不会。D题比较裸的DP但是DP不会果然还是太菜了。 总结,CD确实不会做,以前没遇到过相关性的题,C题偏思维和贪心,D题DP是现在的短板。

游游画U

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
LL a[N],n;
int main(void)
{
    cin>>n;
    for(int i=1;i<=n*4-n;i++)
    {
        for(int j=1;j<=n;j++) cout<<"*";
        for(int j=1;j<=n*2;j++) cout<<".";
        for(int j=1;j<=n;j++) cout<<"*";
        puts("");
    }
    for(int i=1;i<=n;i++)
    {
        int len=n*4-i*2-n*2;
        for(int j=1;j<=i;j++) cout<<".";
        for(int j=1;j<=n;j++) cout<<"*";
        for(int j=1;j<=len;j++) cout<<".";
        for(int j=1;j<=n;j++) cout<<"*";
        for(int j=1;j<=i;j++) cout<<".";
        puts("");
    }
    return 0;
}

游游的数组染色

就是分两拨,这里用mp1,mp2来维护,最后枚举mp1看mp2有不,有的话乘法原理计数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
LL a[N],n;
string s;
map<int,int>mp1,mp2;
int main(void)
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    cin>>s;
    for(int i=0;i<n;i++)
    {
        if(s[i]=='B') mp1[a[i]]++;
        else mp2[a[i]]++;
    }
    LL ans=0;
    for(auto i=mp1.begin();i!=mp1.end();i++)
    {
        int x=i->first,y=i->second;
        if(mp2[x]) ans+=y*mp2[x];
    }
    cout<<ans<<endl;
    return 0;
}

游游的交换字符

image.png

//暴力做法只能拿部分分
#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
string s;
int cnt1=0,cnt0=0;
int f1(string t)
{
    string temp=s;
    string ans;
    while(ans.size()<s.size()) ans+=t;
    int cnt=0;
    for(int i=0;i<temp.size();i++)
    {
        if(ans[i]==temp[i]) continue;
        int j=i+1;
        while(j<temp.size() && temp[j] != ans[i]) j++;
        for(int z=j-1;z>=i;z--) swap(temp[z],temp[z+1]),cnt++;
    }
    return cnt;
}
int f2()
{
    string temp=s;
    string ans=s;
    if(cnt1>cnt0)
    {
        for(int i=0;i<s.size();i+=2) ans[i]='1';
        for(int i=1;i<s.size();i+=2) ans[i]='0';
    }else
    {
         for(int i=0;i<s.size();i+=2) ans[i]='0';
        for(int i=1;i<s.size();i+=2) ans[i]='1';
    }
    int cnt=0;
    for(int i=0;i<temp.size();i++)
    {
        if(ans[i]==temp[i]) continue;
        int j=i+1;
        while(j<temp.size() && temp[j] != ans[i]) j++;
        for(int z=j-1;z>=i;z--) swap(temp[z],temp[z+1]),cnt++;
    }
    return cnt;
}
int main(void)
{
    cin>>s;
    for(int i=0;i<s.size();i++) 
        if(s[i]=='1') cnt1++;
        else cnt0++;
    if(cnt1==cnt0)
    {
        cout<<min(f1("01"),f1("10"));
    }else 
    {
        cout<<f2()<<endl;
    }
    return 0;
}

正解,就是目标是偶数,那么最终的状态就是1010...或者010101..这种计算一个min, 如果目标是奇数,那么如果1的个数多就是10...10...1这种,如果0的个数多最终状态就是01..0..10这种。 最终状态确定了,那么达到这个最终状态需要的步数该如何求。贪心的想,用两个vector存当前和目标的所有为1的下标或者所有为0的下标,计算当前状态的和最终状态的下标差值就是该位置移动到目标位置的最小步数,累加就是总的操作数,1都移动到对应位置了,0不用管自动就是对应的位置了。 其实就是贪心的计算,每一个位置到最终位置最小需要多少步,因为是从小到大顺序遍历的,故就是计算当前位置和目标位置的差值,就是贪心的最小操作数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
string s;
LL cnt1=0,cnt0=0;
LL f1(string t)
{
    string temp=s;
    string ans;
    while(ans.size()<s.size()) ans+=t;
    LL cnt=0;
    vector<int>ve1,ve2;
    for(int i=0;i<temp.size();i++)
        if(temp[i]=='0') ve1.push_back(i);
    for(int i=0;i<ans.size();i++)
        if(ans[i]=='0')  ve2.push_back(i);
    for(int i=0;i<ve1.size();i++)
        cnt+=abs(ve1[i]-ve2[i]);
    return cnt;
}
LL f2()
{
    string temp=s;
    string ans=s;
    if(cnt1>cnt0)
    {
        for(int i=0;i<s.size();i+=2) ans[i]='1';
        for(int i=1;i<s.size();i+=2) ans[i]='0';
    }else
    {
        for(int i=0;i<s.size();i+=2) ans[i]='0';
        for(int i=1;i<s.size();i+=2) ans[i]='1';
    }
    LL cnt=0;
    vector<int>ve1,ve2;
    for(int i=0;i<temp.size();i++)
        if(temp[i]=='0') ve1.push_back(i);
    for(int i=0;i<ans.size();i++)
        if(ans[i]=='0')  ve2.push_back(i);
    for(int i=0;i<ve1.size();i++)
        cnt+=abs(ve1[i]-ve2[i]);
    return cnt;
}
int main(void)
{
    cin>>s;
    for(int i=0;i<s.size();i++) 
        if(s[i]=='1') cnt1++;
        else cnt0++;
    if(cnt1==cnt0) cout<<min(f1("01"),f1("10"));
    else cout<<f2()<<endl;
    return 0;
}

游游的9的倍数

image.png 设f[i][j]为取前i个字符,取一个子序列模9为j的方案数。 这里有一个技巧是,一个数是9的倍数等价于这个数的各个数的和%9等于0。

image.png

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
const int mod=1e9+7;
typedef long long int LL;
LL f[N][10], a[N], n;
string s;
int main(void)
{
    cin >> s;
    n = s.size();
    s = "0" + s;
    for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
    f[0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        // 先传递前一个状态,即该数不选
        for (int j = 0; j < 10; j++)
        {
            f[i][j] = f[i-1][j] % mod;
        }
        // 再更新当前字符对余数的影响,即该数选
        for (int j = 0; j < 10; j++)
        {
            f[i][(j + a[i]) % 9] = (f[i][(j + a[i]) % 9] + f[i-1][j]) % mod;
        }
    }
    cout << (f[n][0] - 1 + mod) % mod;//-1是减去空子序列这种情况
    return 0;
}