2025牛客寒假算法基础集训营2部分题解(蒟蒻版)

1 前言:

大学初涉ACM,如今已经第二次参加牛客寒假集训营力.想当初第一次参加时,完全零经验,一次只能做出5,6题,排个1000开外。如今再来,也有十道题左右力(感觉今年更简单了捏)。

新的一年,大家也要KiraKiraDokiDoki哦!

2 题解

A 一起奏响历史之音!

签到题。用map检查输入数据是否合法即可。(由于有队友极其痛恨map,所以直接写if在读入时判断也行)

void solve()
{
    int ok = 1;
    int tmp;
    for (int i = 1; i <= 7; i++)
    {
        cin >> tmp;
        if (tmp == 4 || tmp == 7)
        {
            ok = 0;
        }
    }
    if (ok == 0)
    {
        cout << "NO" << endl;
    }
    else
    {
        cout << "YES" << endl;
    }
}

B 能去你家蹭口饭吃吗

签到题。按碗的容量排序好后选择第n/2+1个碗,也就是从小到大第一个不合法的碗。将这个碗的值减一就是最大的合法的碗了。

void solve()
{
    int n;
    cin >> n;
    vector<int>arr(n + 5);
    for (int i = 1; i <= n; i++)
    {
        cin >> arr[i];
    }

    sort(arr.begin() + 1, arr.begin() + 1 + n);
    int ans = arr[n / 2 + 1] - 1;
    cout << ans << endl;
}

C 字符串外串

构造题,单独出现需要一些注意力,不过D题给了提示后就很简单了。题意转化后是:对26个字符求——相同字符从前往后的倒数第二个字符的下标,从后往前的倒数第二个字符的reverse下标中大的值——中最大的值记为m。字符串总长记为n

既然是构造题,先分情况:

  1. 一定不可能构造成功的情况:因为要求m不连续子序列所以m最大为n1。所以m==n一定不成立。

  2. 一定可以的情况:m和n接近,可以用形如abcdfgaaaaaaaaaaaaahijklma的形式满足

  3. 检查构造限制:已知m和n相近更容易得到解,那么对于构造优先考虑m最小时应该怎么构造。既然每个字符都单独计算答案并取最大值,那么最简单的办法就是要么确保一个字符一定比其他的大,要么所有字符的答案都是期望的答案。

  4. 考虑可能的解法:显然对于确保一个字符一定比其他的大,其m不是最小情况,因为所有字符的答案都是期望的答案有形如abcdabcdabcda的字符串可以相比apaaaaaaaqa有更优的m/n比,更满足题目限制。所以选择所有字符的答案相同的情况考虑构造。

  5. 构造情况:想要所有字符的答案相同,只要选择适合长度的无重复字符串copy,paste就可以保证,且无重复字符串的长度越长,m/n比就越小。那非法条件也出来了,应为无重复最多26个字符,那题目m/n比需求无重复字符串的长度超出26的就一定不可能。

既然是构造题,那就按思路写好,狠狠地交一发试试长崎罚时导致的。结果真对了,不过实际来说应该检验一下是否覆盖了全部情况的。

string abc = "abcdefghijklmnopqrstuvwxyz";
void solve()
{
    int n, m;
    cin >> n >> m;
    if (n == m )
    {
        cout << "NO" << endl;
        return;
    }

    int delta = n - m;
    if (delta > 26)
    {
        cout << "NO" << endl;
        return;
    }

    cout << "YES" << endl;
    string ans;

    for (int i = 1; i <= n; i++)
    {
        ans.push_back('a' + i % (delta));
    }

    cout << ans;

    cout << endl;


}

D 字符串里串

注意力题,简单题。注意到测试样例里abcc只有最后一个需要分离构成不连续子序列,那考虑对26个字符求——相同字符从前往后的倒数第二个字符的下标,从后往前的倒数第二个字符的reverse下标中大的值——中最大的值即可。

map<char, pair<int, int>>pre;
map<char, pair<int, int>>rev;
void solve()
{
    int n;
    cin >> n;
    string str;
    cin >> str;
    str = "@" + str +'@';
    int ans = 0;

    for (int i = 1; i <= n; i++)
    {
        int nowch = str[i];
        if (pre[nowch].first == 0)
        {
            pre[nowch].first = i;

        }
        else if(pre[nowch].second == 0)
        {

            pre[nowch].second = i;
        }
        else
        {
            pre[nowch].first = pre[nowch].second;
            pre[nowch].second = i;
        }
    }

    for (char i = 'a'; i <= 'z'; i++)
    {
        if (pre[i].second != 0)
        {
            ans = max(ans, pre[i].first);
        }
    }

    reverse(str.begin(), str.end());

    for (int i = 1; i <= n; i++)
    {
        int nowch = str[i];
        if (rev[nowch].first == 0)
        {
            rev[nowch].first = i;

        }
        else if (rev[nowch].second == 0)
        {

            rev[nowch].second = i;
        }
        else
        {
            rev[nowch].first = rev[nowch].second;
            rev[nowch].second = i;
        }
    }

    for (char i = 'a'; i <= 'z'; i++)
    {
        if (rev[i].second != 0)
        {
            ans = max(ans, rev[i].first);
        }
    }

    cout << ans << endl;

}

E 一起走很长的路!

队友做了,待会PO上来。

F 一起找神秘的数!

简单数论。

x 为其中较大的一个,对于x,y,满足以下条件:

x(xy)x ,特别的,x=y 时,(xy)=x ;

x(xy)+(xy)0 特别的,x=y 时,(xy)=x,(xy)=0 ;

所以 x+y(xy)+(xy)+(xy) ;

当且仅当x=y时, x+y=(xy)+(xy)+(xy) ;

所以直接取l,r的区间中的整数数量即可。

void solve()
{
    int l, r;
    cin >> l >> r;
    int ans = r - l + 1;
    cout << ans << endl;
}

G 一起铸最好的剑!

签到题,不过队友甚至罚了两发。从一开始递增遍历m的幂直到满足题意即可,记得考虑m=1的情况。

void solve()
{
    int n, m;
    cin >> n >> m;

    int now = m;
    int delta = abs(n - m);
    int ans = 1;
    for (int i = 2; i <= 30; i++)
    {
        now = now * m;
        int tmpdelta = abs(n - now);
        if (tmpdelta < delta)
        {
            delta = tmpdelta;
            ans = i;
        }
        else
        {
            break;
        }
    }

    cout << ans << endl;
}

H 一起画很大的圆!

几何题?注意力题!卡我半天,其实在一个矩形上,要使做的圆面积最大,也就是半径最大。先取任意两点连线,并作中垂线,再取第三点,与其他两点连线作中垂线,找到圆心,摆弄一下就注意到,似乎两点间对角线最大,然后取一个尽可能接近的第三点垂下去半径最大?但这样两垂线间夹角还可以再缩小,取矩形左上角和右上角减一格,再取长边侧与左上角距离为一的点才是最优。

没有证明,只有摆弄。。。

void solve()
{
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    
    int len = b - a + 1;
    int high = d - c + 1;
    pair<int, int>lu = make_pair(d, a);
    pair<int, int>rd;

    if (len > high)
    {
        rd = make_pair(d - 1, b);
    }
    else
    {
        rd = make_pair(c, a+1);
    }

    cout << lu.second << " " << lu.first << endl;
    cout << rd.second << " " << rd.first << endl;

    if (len > high)
    {
        cout << lu.second + 1 << " " << lu.first<<endl;
    }
    else
    {
        cout << lu.second  << " " << lu.first -1 <<endl;
    }

}

J 数据时间?

纯模拟。已黑化,不会再爱了。

int ansa = 0, ansb = 0, ansc = 0;

bool bet(int hh, int mm, int ss, int start_h, int start_m, int start_s, int end_h, int end_m, int end_s) {
    int now = hh * 3600 + mm * 60 + ss;
    int start_time = start_h * 3600 + start_m * 60 + start_s;
    int end_time = end_h * 3600 + end_m * 60 + end_s;
    if (start_time > end_time) 
    {
        return now >= start_time || now <= end_time;
    }
    return now >= start_time && now <= end_time;
}

map<int, map<string, int>>timesection_id;
string uid;
string ymd;
string hms;
void f(const std::string& hms) {
    int hh = 0, mm = 0, ss = 0;
    char de;
    std::istringstream timeStream(hms);
    timeStream >> hh >> de >> mm >> de >> ss;
    if (bet(hh, mm, ss, 7, 0, 0, 9, 0, 0) || bet(hh, mm, ss, 18, 0, 0, 20, 0, 0)) 
    {
        timesection_id[1][uid]++;
    }
    else if (bet(hh, mm, ss, 11, 0, 0, 13, 0, 0)) 
    {
        timesection_id[2][uid]++;
    }
    else if (bet(hh, mm, ss, 22, 0, 0, 1, 0, 0)) 
    {
        timesection_id[3][uid]++;
    }
}
void solve()
{
    int n;
    string h, m;
    cin >> n >> h >> m;
    if (h.size() == 1)
    {
        h = "0" + h;
    }
    if (m.size() == 1)
    {
        m = "0" + m;
    }
    map<string, int>bolls;
    
    for (int i = 1; i <= n; i++)
    {
        
        cin >> uid >> ymd >> hms;
        string tmpsum = uid + ymd + hms;
        if (bolls[tmpsum] != 0)
        {
            continue;
        }
        else
        {
            bolls[tmpsum]++;
        }
        int check = 1;
        for (int j = 0; j < 4; j++)
        {
            if (h[j] != ymd[j])
            {
                check = 0;
                break;
            }
        }
        for (int j = 5; j < 7; j++)
        {
            if (m[j-5] != ymd[j])
            {
                check = 0;
                break;
            }
        }

        if (check == 1)
        {

            f(hms);
        }

    }

    cout << timesection_id[1].size() << " " << timesection_id[2].size() << " " << timesection_id[3].size() << endl;
}

K 可以分开吗?

dfs,经典老题。建个操作用图,为每个连通分量标记好编号即可。

int n, m;
int croods[505][505];
int usecr[505][505];
int cnt = 10;
pair<int, int> wasd[4] = { {-1, 0},{0 ,-1},{1, 0},{0, 1} };

map<int, int>cntcost;


int legalij(int nowi, int nowj)
{
    if (nowi > 0 && nowi <= n)
    {
        if (nowj > 0 && nowj <= m)
        {
            return 1;
        }
    }
    return 0;
}
int addcost(int nowi, int nowj)
{
    for (int i = 0; i <= 3; i++)
    {
        int newi = nowi + wasd[i].first;
        int newj = nowj + wasd[i].second;

        if (legalij(newi, newj))
        {
            if (usecr[newi][newj] !=0 && usecr[newi][newj] != cnt+100005 && usecr[newi][newj]>= 100005)
            {
                usecr[newi][newj] = cnt + 100005;
                cntcost[cnt]++;
            }
        }
    }
    return 1;
}

int ans = INF;
int dfs(int nowi, int nowj)
{
    usecr[nowi][nowj] = cnt;
    addcost(nowi, nowj);

    for (int i = 0; i <= 3; i++)
    {
        int newi = nowi + wasd[i].first;
        int newj = nowj + wasd[i].second;

        if (legalij(newi, newj) && usecr[newi][newj]!=cnt && usecr[newi][newj] < 100005)
        {
            dfs(newi, newj);
        }
    }
    return 114;
}

void solve()
{
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        string tmp;
        cin >> tmp;
        for (int j = 1; j <= m; j++)
        {
            if (tmp[j - 1] == '0')
            {
                croods[i][j] = 100005;
            }
            else
            {
                croods[i][j] = 1;
            }
            
            usecr[i][j] = croods[i][j];
        }
        
    }

    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (usecr[i][j] == 1)
            {
                cnt++;
                dfs(i, j);
                ans = min(ans, cntcost[cnt]);
            }
        }
    }
    cout << ans << endl;
}

3 总结

难度偏简单,但过年氛围使得大家都挺懒散。不敢想现在都这样摸鱼,下一场在年前会寄成什么样子。

posted @   青一凡  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示