华为OD 数字游戏

1. 题意

题目描述

小明玩一个游戏:

系统发 1+n 张牌,每张牌上有一个整数。

第一张给小明(数字为 m);

后 n 张按发牌顺序排成连续一行。

需要判断:后 n 张牌中,是否存在连续的若干张牌,其和能整除小明手中牌的数字 m。

输入输出

输入

数据有多组,每组占两行,输入至文件结尾:

第一行:两个整数 n 和 m(空格分隔,m 是小明手中牌的数字);

第二行:n 个整数(空格分隔,代表后 n 张牌的数字)。

输出

对每组输入,若存在符合条件的连续子数组,输出 1;否则输出 0。

备注

数据范围:

1 ≤ n ≤ 1000;

牌上的整数 ≤ 400000;

输入组数 ≤ 1000;

输入格式保证正确,无需处理非法情况。

2. 题解

2.1 动态规划

自己想到的解法。定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示以 i i i个数为结尾的所有子串中是否存在和模 m m m为 j j j的子串。

状态转移方程
d p [ i + 1 ] [ ( j + a [ i ] )   m o d   m ] = d p [ i ] [ j ] dp[i+1][(j+a[i])\bmod m] =dp[i][j] dp[i+1][(j+a[i])modm]=dp[i][j]

  • 代码一
cpp 复制代码
int n,m;
int ans;

int dp[1001][1001];
void solve1_1()
{
    memset(dp, 0, sizeof(dp));
    ans = 0;

    vector<int> a( n + 1, 0 );
    for (int i = 1;i <= n; ++i) {
        cin >> a[i];
    }

    dp[0][0] = 1;
    for (int i = 1; i <= n; ++i) {

        int vmod = a[i] % m;
        dp[i][vmod] = 1;
        for (int j = 1; j < m; ++j) {
            if (dp[i - 1][j]) {
                dp[i][ (vmod + j) % m] = dp[i - 1][j];
            }
        }
        if (dp[i][0]) {
            ans = 1;
            break;
        }
    }
}

时间复杂度 O ( n m ) O(nm) O(nm), 空间复杂度 O ( n m ) O(nm) O(nm)

由于我们只需要知道上一个状态,因此可以优化空间复杂度

  • 代码二
cpp 复制代码
void solve1_2()
{
    vector<int> dp(m,0);
    vector<int> ndp(m, 0);

    ans = 0;

    vector<int> a( n + 1, 0 );
    for (int i = 1;i <= n; ++i) {
        cin >> a[i];
    }

    dp[0] = 1;
    for (int i = 1; i <= n; ++i) {

        int vmod = a[i] % m;
        ndp[vmod] = 1;

        for (int j = 0; j < m; ++j) {
            if (dp[j]) {
                ndp[(j + vmod) % m] = dp[j];
            }
        }
        dp = ndp;

        if (dp[0]) {
            ans = 1;
            break;
        }
    }
}

时间复杂度 O ( n m ) O(nm) O(nm), 空间复杂度 O ( m ) O(m) O(m)

2.2 前缀和

其实看了前缀和,也没有想到哈希表继续优化。

因此一开始只是写了前缀和加暴力枚举子串。

  • 代码三
cpp 复制代码
void solve2_1()
{

    ans = 0;
    vector<int> a(n, 0);
    vector<int> pre(n + 1, 0);
    int sum = 0;

    for (int i = 0;i < n; ++i) {
        
        cin >> a[ i ];
        sum  = ( sum + a[i] ) % m;
        pre[ i + 1] = sum;
    }
    for (int i = 0; i < n; ++i) {
        for (int j = i + 1;j <= n; ++j) {
            int dif = (pre[j] + m - pre[i]) % m ;
            if ( 0 == dif) {
                ans = 1;
                break;
            }
        }
        if (ans) break;
    }
}

这道题目找到的最优解,是前缀和加哈希表。

我们定义前缀和pre

p r e [ i ] = ∑ j = 0 i − 1 a [ i ] ∑ k = i j a [ i ] = p r e [ j + 1 ] − p r e [ i ] pre[i]=\sum_{j=0}^{i-1}a[i]\\ \sum_{k=i}^j a[i] =pre[j+1] -pre[i] pre[i]=j=0∑i−1a[i]k=i∑ja[i]=pre[j+1]−pre[i]

其实是利用了同余的性质,如果存在区间 [ i , j ] [i,j] [i,j]满足其和是 m m m的倍数,

那么一定有 p r e [ j + 1 ] − p r e [ i ] = k m pre[j+1] -pre[i]= km pre[j+1]−pre[i]=km,

因此 p r e [ j + 1 ] ≡ p r e [ i ] (   m o d   m ) pre[j+1] \equiv pre[i]\quad (\ \bmod \ m) pre[j+1]≡pre[i]( mod m)。

我们用哈希表记录下 p r e [ k ]   m o d   m pre[k] \bmod m pre[k]modm的值,如果后续出现了的相同的值 p r e [ k ′ ]   m o d   m pre[k'] \bmod m pre[k′]modm。那么说明区间存在。

利用同余性质将找区间转换成了,找相同的模值。

p r e [ k ]   m o d   m = p r e [ k ′ ]   m o d   m pre[k] \bmod m =pre[k'] \bmod m pre[k]modm=pre[k′]modm

  • 代码四
cpp 复制代码
void solve2_2()
{

    vector<int> a(n, 0);
    // vector<int> pre(n + 1, 0);
    int sum = 0;
    unordered_set<int> pos;
    pos.insert(0);


    for (int i = 0;i < n; ++i) {
        cin >> a[ i ];
        sum  = ( sum + a[i] ) % m;
        if ( pos.count(sum)) {
            ans = 1;
        }
        else {
            pos.insert( sum );
        }
    }
}

时间复杂度 O ( n ) O(n) O(n), 空间复杂度 O ( m ) O(m) O(m)

3. 参考

KJ.JK

相关推荐
_不会dp不改名_13 小时前
华为OD 二维伞的雨滴效应
华为od
YOLO大师3 天前
华为OD机试 2025B卷 - 小明减肥(C++&Python&JAVA&JS&C语言)
c++·python·华为od·华为od机试·华为od2025b卷·华为机试2025b卷·华为od机试2025b卷
哪 吒5 天前
2025B卷 - 华为OD机试七日集训第5期 - 按算法分类,由易到难,循序渐进,玩转OD(Python/JS/C/C++)
python·算法·华为od·华为od机试·2025b卷
蜗牛的旷野17 天前
华为OD机试_2025 B卷_矩形相交的面积(Python,100分)(附详细解题思路)
开发语言·python·华为od
_不会dp不改名_18 天前
华为OD 最小循环子数组
算法·华为od·kmp
m0_6407435619 天前
华为OD-2024年E卷-字符串化繁为简[200分] -- python
python·华为od
小猫咪怎么会有坏心思呢19 天前
华为OD机考-生成哈夫曼树-二叉树(JAVA 2025B卷)
java·开发语言·华为od
小猫咪怎么会有坏心思呢19 天前
华为OD机试-云短信平台优惠活动-完全背包(JAVA 2024E卷)
java·开发语言·华为od
小猫咪怎么会有坏心思呢19 天前
华为OD机考-小明减肥-DFS(JAVA 2025B卷)
java·华为od·深度优先