5.27 JLU校赛部分题解

这篇博客介绍了几道编程竞赛题目,包括字符串中特定子串出现次数的计算、物品分配的最优化问题、简单的博弈策略分析、序列的字典序最小排序构造以及几何问题的解决方法。涉及暴力求解、二分查找、博弈论和几何交点计算等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A
给定一个字符串,找出这个字符串中出现了多少次“tjmts”
字符串最大长度1e4

长度短,可暴力。或者kmp

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 10009;
int main()
{
    int t;
    int nt[MAX];
    char s[MAX], w[MAX] = "tjmts";
    int i, j, len1;
    int lenw = strlen(w);
    i = 0;
    j = -1;
    nt[0] = -1;
    while (i < lenw) {
        if (j == -1 || w[i] == w[j]) {
            nt[++ i] = ++ j;
        } else {
            j = nt[j];
        }
    }
    scanf("%d",&t);
    while (t --) {
        int len;
        scanf("%d %s", &len, s);
        int ans = 0;
//      int len = strlen(s);
        int i = 0; j = 0;
        while (i < len) {
            if (s[i] == w[j] || j == -1) {
                i ++; j ++;
            } else {
                j = nt[j];
            }
            if (j == lenw) {
                ans ++;
                j = nt[j];
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

D
有一些物品,一共n种,k个人分,要求每个人分到的物品必须来自同一类,并且,这k个人分到的物品数必须是相同的,求每个人最多能分到多少物品
n最大是1000,一种物品最多1e8个,k最大1e9

没有什么直接找到答案的好办法,但对于给定的一个值容易判断是否是一个合法的值,并且如果k个人每人可以得到m个,那么m-1一定是一个合法的值,如果p是一个不合法的值,那么p+1一定不合法。n的值比较小,验证一个值是否合法的代价不大,所以,可以二分答案,答案就是所有合法值中的最大者。

坑点是答案可能是0,二分时中值如果是0,判断时就会出现除0,特判一下就可以了。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
int n, k, a[1010];
bool ok(int x)
{
    int cnt = 0;
    for (int i = 1; i <= n; i ++) {
        cnt += a[i] / x;
        if (cnt >= k)
            return true;
    }
    return false;
}
int bin(int l, int r)
{
    int mid, ans;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (mid == 0) {
            ans = 0;
            l = 1;
            continue;
        }
        if (ok(mid)) {
            l = mid + 1;
            ans = mid;
        } else {
            r = mid - 1;
        }
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d", &t);
    while (t --) {
        scanf("%d%d",&n,&k);
        for (int i = 1 ; i <= n; i ++) {
            scanf("%d", &a[i]);
        }
        printf("%d\n", bin(0, 1e8 + 1));
    }
    return 0;
}

F
简单博弈题,对于一个给定的数,你可以对这个数做减法,要求减数必须是当前这个数的非零的某一位,先把数字减到0的胜。

对于所有非零个位数,可以一步减到0,是必胜态,10只能减到9,是必败态,而11 ~ 19都可以一步到达10,所以都是必胜态,同理20是必败态……故末位为0是必败态,非0是必胜态。

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

int main()
{
    int t, n;
    scanf("%d", &t);
    while (t --) {
        scanf("%d", &n);
        if (n % 10 == 0) {
            puts("2");
        } else {
            puts("1");
        }
    }
    return 0;
}

B
题意很简单,一个长度为n的序列,要求找出一种排序,这个序列的最大上升子序列长度为m,最大下降子序列为k,如果存在多种,输出字典序最小的一种,如果不存在,则输出一个空行。

难度就在字典序最小上。可以这样构造,先从1开始考虑,先排一段上升序列,然后再排下降序列(下降序列可以排列成多个块),这样可以保证字典序最小,但同时排序必须合法。
例如
n = 48 m = 7 k = 17
1 2 3 4 14 13 12 11 10 9 8 7 6 5 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 48 47
46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
下降的序列可以有多个,观察可知,k的值就是下降的序列中最长序列的长度,m就是上升序列长度+下降序列个数。
因为n最大为50,所以可以枚举开始上升序列的长度,则对应长度需要固定数量的下降序列,试填之,如果找到合法序列,输出,找不到则说明不存在。
注意细节问题,比如说上升序列长度可以为0,特殊情况的特判等等

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

int main()
{
    int t, n, m, k, a[110];
    scanf("%d", &t);
    while (t --) {
        scanf("%d%d%d", &n, &m, &k);
        if (k <= 0 || m <= 0) {
            printf("\n");
            continue;
        }
        if (k > n || m > n) {
            printf("\n");
            continue;
        }
        if (n == 3 && (k == 1 && m == 2 || m == 1 && k == 2)) {
            printf("\n");
            continue;
        }
        if (n == 1) {
            printf("1\n");
            continue;
        }
        if (n == 3 && k == 1 && m == 3) {
            puts("1 2 3");
            continue;
        }
        if (n == 3 && k == 3 && m == 1) {
            puts("3 2 1");
            continue;
        }
        int fflag = 0;
        for (int ii = 1; ii <= m ; ii ++)
        {
            int cnt = 0;

            for (int i = 1; i <= m - ii; i ++) {
                a[++ cnt] = i;
            }
            int sum = cnt;
//          printf("%d : %d \n", ii, cnt);
            if (n - sum < k) {
                continue;
            }

            int loc = n;
            int time = 1;
            int key ;
            while (k < n - cnt) {
                int i;
                for (i = n - time * k + 1; i <= n - (time - 1) * k; i ++) {
                    a[i] = n + n - i - time * k + 1 - (time - 1) * k;
                    ++ cnt;
                }
                loc = n - time * k;
                -- i;
                key = 2 * n - i - time * k - (time - 1) * k;
                ++ time;
            }

            for (int i = sum + 1; i <= loc; i ++) {
                a[i] = key --;
            }
            ++ key;
            if (sum == 0) a[sum] = 0;
            if (a[sum] + 1 != key || time != ii) {
//              printf("%d %d\n", time, ii);
//              printf("%d %d\n", a[sum], key);
//              printf("%d\n", sum);
                continue;
            }
            fflag = 1;
            printf("%d", a[1]);
            for (int i = 2; i <= n ; i ++) {
                printf(" %d", a[i]);
            }
            printf("\n");
            break;
        }
        if (!fflag) {
            printf("\n");
        }
    }
    return 0;
}

G
这个题现场没做出来,没读懂题,也没带字典,后来知道题意之后发现原来不难。题意是对于一个给定的矩形和给定的P点,按顺时针过矩形的顶点和P点做直线,过 顺时针序的上一个顶点过这条直线的垂线,问如此做出的四条垂线是否交与一点。
题目规定P点不与矩形顶点垂直,所以不存在平行的情况。先求出两条垂线的交点,再判断这个点在不在另外两条垂线上即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值