牛客小月赛41 (A~F)题解

今天是lzgg出的题呀,是小红专场;题目不是很难,但是比赛时候网站老是崩,体验就不太舒服;

A、小红的签到题

题目传送门

题意: a 道题,一共有 b 人参赛,所有人通过题目数量的总数为 c 道,问最多有多少人全部写完;

解题思路:简单签到题,这题差点就抢到首A了,QAQ;直接用c除以a不计算余数就是答案;

AC代码:

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    int a, b, c;
    cin>>a>>b>>c;
    cout<<c/a<<endl;
    return 0;
}

B、小红的ABC

题目传送门

题意:给你一个字符串s判断这个字符串的子串中长度超过1的最短的回文串长度是多少,如果没有,输出-1;

解题思路:看一眼数据范围,字符串长度不超过100,这数据范围还要啥自行车,直接暴力枚举每一个长度大于2的字串,看它是不是回文串,然后找到最小的长度;

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    string s;
    cin>>s;
    int n = s.length();
    int ans = 0x3f3f3f3f;//维护子串长度最小的回文串长度
    string c, t;
    for(int i = 0; i<n; i++){
        for(int j = i+1; j<n; j++){
            c = "", t = "";
            for(int k = i; k<=j; k++) c+=s[k], t+=s[j-k+i];
            // for(int k = c.length()-1; k>=0; i--) t+=c[k];
            // cout<<c<<endl;
            // cout<<t<<"***"<<endl;
            if(c == t){//判断回文串
                ans = min(ans, j-i+1);
            }
        }
    }
    if(ans == 0x3f3f3f3f) puts("-1");
    else cout<<ans<<endl;
    return 0;
}

C、小红的口罩

题目传送门

题意:换种说法讲这个题意,小红能吃下重量为k的食物,现在有n个食物,每个食物重量是a[i],小红每吃一个重量为x的食物,就会多出一个重量为2*x的食物,问小红最多能吃多少个食物;

解题思路:这题首先可以看出每次都是贪心得取最小的,而如果直接用数组存储暴力模拟每次取最小的,时间复杂度就是O(nnk),一定会超时,所以我们可以选择用小根堆去存储,使得插入、删除都是longn的;关于小根堆可以直接用stl中的priority_queue,详细用法自行查询;

AC代码:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;

struct cmp1
{
    bool operator ()(int &a,int &b)
    {
        return a>b;
    }
};
priority_queue <int,vector<int>, cmp1> q;//小根堆

const int N = 100005;

int main()
{
    int n, k, x;
    scanf("%d%d", &n, &k);
    for(int i = 1; i<=n; i++){
        scanf("%d", &x);
        q.push(x);
    }
    int ans = 0;
    while(k&&!q.empty()){
        int t = q.top();
        q.pop();
        if(t>k) break;
        k-=t;
        ans++;
        if((ll)t*2<=1e9){
            q.push(t*2);
        }
    }
    cout<<ans<<endl;
    return 0;
}

D、小红的数组

题目传送门

题意:小红有一个长度为n的数组a,求出同时在数组里取两个数,分别输出他们的乘积大于k,等于k,小于k的方案数;

解题思路:按照lzgg给出的官方题解,这题可以用二分和双指针来解决,我是用二分来解决的,当然二分需要注意边界条件,我在这里吃了两次亏;二分的前提就是对给出的数组进行从小到大的排序,排完序后对于每个a[i],我们需要二分查找的是k/a[i]的边界(可以直接用stl中的二分函数),这里注意要分类讨论能否进行整除;而我们要求的三种情况:

1、当乘积等于k时:这种情况只有在k能整除a[i],且数组a中含有k/a[i]的时候,才会存在,数量为k/a[i]的数量,可以提前用map存一下数量,注意当a[i] == k/a[i]的时候,需要减去1(因为一次不能同时取一个位置上的数);

2、当乘积大于k的时候:这种情况下,若是k能整除a[i],找到第一个大于k/a[i]的数的位置(upper_bound())tot,后面的n-tot+1个数和他组合满足此情况,注意判断此时a[i]是否在tot后面,若是则方案数减1(因为一次不能同时取一个位置上的数);k不能整除a[i]的时候类似;

3、当乘积小于k的时候:这种情况与第二种情况相反,就不多赘述;

此时利用二分求出来的答案,会造成相同两个位置上的数【x,y】取一次【y,x】取一次,这样会使得最终的答案恰好是标准答案的两倍,最后的答案需要除以2;

AC代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;

const int N = 300005;

ll a[N];

map<ll, int> ma;

int main()
{
    int n;ll k;
    scanf("%d%lld", &n, &k);
    for(int i = 1; i<=n; i++) scanf("%lld", &a[i]), ma[a[i]]++;
    sort(a+1, a+n+1);
    ll t;
    ll ans = 0, res = 0, num = 0;
    a[n+1] = 0x3f3f3f3f;
    for(int i = 1; i<=n; i++){
        t = a[i];
        ll p = k/t;
        if(k%t == 0){
            int tot = lower_bound(a+1, a+n+1, p) - a;
            if(p == a[tot]) {
                ans+=ma[p];
                if(p == a[tot]) ans--;
            }
            res+=tot-1; if(t<a[tot]) res--;
            tot = upper_bound(a+1, a+n+1, p)-a;
            num+=n - tot + 1; if(t>=a[tot]) num--;
        }
        else{
            int tot = upper_bound(a+1, a+n+1, p) - a;
            res+=tot-1; if(t<a[tot]) res--;
            num+=n-tot+1; if(t>=a[tot]) num--;
        }
    }
    cout<<num/2ll<<' '<<ans/2ll<<' '<<res/2ll<<endl;
    return 0;
}

E、小红的rpg游戏

题目传送门

题意:给你一个nm大小的地图,小红需要从左上角走到右下角,途中有’.‘道路可以直接走,’'围墙不能走,‘0~9的数字’,表示走这个点需要花费给定数值的血量,小红有h滴血,求小红从左上角走到右下角最少需要走多少个格子(注:数字格子不超过10个);

解题思路:如果没有数字格子,那么这个题目就是一个经典的bfs最短路问题,但是题目中给定的条件数字格子不超过10个,我们就可以枚举每个数字格子选还是不选,如果不选,那么这个数字格子就可以看作是一个’*’,无法通行,如果选择,则需要判断小红的血量h能否撑过所有的数字格子,对枚举的每一种状态做dfs就行,因为枚举最多10个格子选或不选,就可以二进制状压枚举;

昨晚眼瞎没看到格子数不超过10个,一直在想dp做法,结果F题也没看,dp永远的痛;

AC代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;

const int N = 100;
string s[N];
int v[N][N];
struct lq{
    int x, y, h;
    lq(int x, int y, int h): x(x), y(y), h(h){}
};
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int n, m, h;
vector<lq>a;

int main()
{
    scanf("%d%d%d", &n, &m, &h);
    for(int i = 0; i<n; i++) cin>>s[i];
    for(int i = 0; i<n; i++){
        for(int j = 0; j<m; j++){
            if(s[i][j]>='0'&&s[i][j]<='9'){
                a.push_back(lq(i, j, s[i][j] - '0'));
            }
        }
    }
    int ans = 0x3f3f3f3f;
    for(int i = 0; i<1<<a.size(); i++){
        int sum = 0;
        for(int j = 0; j<a.size(); j++){
            if((1<<j)&i) v[a[j].x][a[j].y] = 1, sum+=a[j].h;
            else v[a[j].x][a[j].y] = 0;
        }
        if(sum>=h) continue;
        queue<pair<int,int> >q;
        q.push({0,0});
        int st[N][N] = {};
        memset(st, -1, sizeof st);
        st[0][0] = 0;
        while(!q.empty()){
            pair<int, int>t = q.front();
            q.pop();
            for(int j = 0; j<4; j++){
                int x = t.first+dx[j], y = t.second+dy[j];
                if(x<0||x>=n||y<0||y>=m||st[x][y]!=-1) continue;
                if(s[x][y] == '.'||(s[x][y]!='*'&&v[x][y])){
                    st[x][y] = st[t.first][t.second]+1;
                    q.push({x, y});
                }
            }
        }
        if(st[n-1][m-1]!=-1) ans = min(ans, st[n-1][m-1]);
    }
    if(ans == 0x3f3f3f3f) puts("-1");
    else cout<<ans<<endl;
    return 0;
}

F、小红的375

题目传送门

题意:给出一个位数长度为300000的数字,对它的各个位数进行重新排列,使得重排后的数能够被375整除,输出重排后的数(不包含前导0),否则输出-1;

解题思路:375可以拆分成3*125,即重排后的数字要同时是3和125的倍数,注意数字位数过大;

对于3很简单,各个位数相加的和如果能被3整除,那么这个数也能被3整除;

对于125,125的特征是一个125的循环是1000,简单说就是枚举三位内125的倍数,若一个数最后三位数是125的倍数,则这个数能被125整除;枚举方法可以用桶去存储,查看桶中剩余的数能否构成一个三位数的125的倍数(注意考虑前导0);

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

typedef long long ll;

int st[50];

// string check[8] = {"125", "250", "375", "500", "625", "750", "875", "000"};
string check[8]={"500","000","750","250","125","375","625","875"};//这里把0结尾的放在前面,预防前导0;
int main()
{
    string s;
    cin>>s;
    int len = s.length();
    ll ans = 0;
    for(int i = 0; i<len; i++){
        st[s[i] - '0']++;
        ans += s[i] - '0';
    }
    if(ans%3!=0){
        puts("-1");
        return 0;
    }
    for(int i = 0; i<8; i++){
        for(int j = 0; j<3; j++){
            st[check[i][j] - '0']--;
        }
        bool flag = 1;
        for(int j = 0; j<=9; j++){
            if(st[j]<0){
                flag = 0;
                break;
            }
        }
        if(flag){
            for(int j=9;j>=0;j--)while(st[j])printf("%d", j),st[j]--;
            cout<<check[i];
            return 0;
        }
        for(int j = 0; j<3; j++){
            st[check[i][j] - '0']++;
        }
    }
    puts("-1");
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值