暑期算法帖

一些算法碎碎念-不定时更新

最新一次更新:2025/7/1,更新《数独合理吗?》

[NOIP2018]标题统计

链接:https://ac.nowcoder.com/acm/contest/19305/1016
来源:牛客网

凯刚写了一篇美妙的作文,请问这篇作文的标题中有多少个字符?
注意:标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字 符数时,空格和换行符不计算在内


输入描述:

输入文件只有一行, 一个字符串s。

输出描述:

输出文件只有一行,包含一个整数,即作文标题的字符数(不含空格和换行符)。

示例1

输入

234

输出

3

说明

标题中共有 3 个字符,这 3 个字符都是数字字符。

示例2

输入

Ca 45

输出

4

说明

标题中共有 5 个字符,包括 1 个大写英文字母,1 个小写英文字母和 2 个数字字符,
还有 1 个空格。由于空格不计入结果中,故标题的有效字符数为 4 个。

解:

#include <iostream>
using namespace std;
string t;
int main(){
    getline(cin, t);
    while( t.find(' ') != string::npos){
        t.erase(t.find(' '),1);
    }
    cout<<t.length();
}

string中find()返回值是字母在母串中的下标位置。
如果没有找到,那么会返回一个特别的标记npos,一般写作string::npos

说好听点叫练习函数用法,看上去也很简洁,但是感觉不如:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    char s;
    int n=0;
    while(cin>>s)
    {
        n++;
        if(s==' '){n--;}
    }
    cout<<n;
    return 0;
}

(摊手)

2025/7/1更新

[数独合理吗?]

链接:https://ac.nowcoder.com/acm/contest/19306/1025
来源:牛客网

众所周知,数独是一款简单上手(划掉)且极易打发时间的游戏,
fishfloss喜欢玩数独,虽然自己很菜。
数独的具体规则是这样的:
需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,

并满足每一行、每一列、每一宫(3x3)内的数字均含1-9并不重复


那么下面给出数独盘面,来判断一下它是否是数独的合法解吧

输入描述:

输入共9行,每行9个数,代表数独盘面

输出描述:

一行,如果输入是数独的合法解,则输出YES
反之输出NO

链接:https://ac.nowcoder.com/acm/contest/19306/1025
来源:牛客网

示例1

输入

1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
7 8 9 1 2 3 4 5 6
2 3 1 5 6 4 8 9 7
5 6 4 8 9 7 2 3 1
8 9 7 2 3 1 5 6 4
3 1 2 6 4 5 9 7 8
6 4 5 9 7 8 3 1 2
9 7 8 3 1 2 6 4 5

输出

YES

示例2

输入

1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
7 8 9 1 2 3 4 5 6
2 3 1 5 6 4 8 9 7
5 6 4 8 9 7 2 3 1
8 9 7 2 3 1 5 6 4
3 1 2 6 4 5 9 7 8
6 4 5 9 7 8 3 1 2
9 7 8 3 1 2 6 4 4

输出

NO

说明

对于宫9,行9及列9,显然有重复的4,故不满足合法解

解:

好,该给大家看看屎了(反正是我自己的网站不怕丢人)

我写之前以为这种写法没准会快不少,而且能少写点代码,抱着这样的心态我得到了:

(其实可以跳过不看,这段代码不是本文重点)

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a[22]={0},temp;
    for(int i=1;i<=9;i++){
        for(int i2=1;i2<=9;i2++){
            cin>>temp;
            a[12+temp]+=1;
            a[i2]+=temp; 
            a[0]+=temp;
            if (i2<4){
                a[10]+=temp;
            }
            else if(i2<7){
                a[11]+=temp;
            }
            else {
                a[12]+=temp;
            }
        }
        if (a[0]!=45){
            cout<<"NO";
            return 0;
        }
        else{
            a[0]=0;
        }
        if (i%3==0){
            if(a[10]!=45 || a[11]!=45 || a[12]!=45){
                cout<<"NO";
                return 0;
            }
            else{
                a[10]=a[11]=a[12]=0;
            }
        }
    }
    for(int i2=1;i2<=9;i2++){
        if(a[i2]!=45){
            cout<<"NO";
                return 0;
        }
    }
    for(int i2=13;i2<=21;i2++){
        if(a[i2]!=9){
            cout<<"NO";
                return 0;
        }
    }
    cout<<"YES";
}

没有任何想看下去的欲望,对吧()

结果花了整整4毫秒,1058长(合着一个没捞着,人家都是3毫秒,而且用传统方法700+长就可以写完)

紧接着我看到了一个神仙:

#include<bits/stdc++.h>
using namespace std;
 
set<int> s1[10], s2[10], s3[10];
int a[9][9];
 
int main() {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> a[i][j];
            s1[i].insert(a[i][j]);
            s2[j].insert(a[i][j]);
            s3[i / 3 * 3 + j / 3].insert(a[i][j]);
        }
    }
 
    for (int i = 0; i < 9; i++) {
        if (s1[i].size() != 9 || s2[i].size() != 9 || s3[i].size() != 9) {
            cout << "NO";
            return 0;
        }
    }
    cout << "YES";
     
    return 0;
}

3ms,457长,人傻了我,甚至还return 0;

废话不多说,这里贴上ai的带注释版本和解析:

#include<bits/stdc++.h>
using namespace std;

// 定义三个集合数组用于存储数独的三种约束:
set<int> s1[10];  // s1[i] 存储第 i 行出现的数字
set<int> s2[10];  // s2[i] 存储第 i 列出现的数字
set<int> s3[10];  // s3[i] 存储第 i 个宫块(3x3子网格)出现的数字
int a[9][9];  // 存储数独矩阵的二维数组

int main() {
    // 读取数独输入并初始化集合
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> a[i][j];  // 读取当前位置的数字
            
            // 将数字添加到对应行的集合
            s1[i].insert(a[i][j]);
            
            // 将数字添加到对应列的集合
            s2[j].insert(a[i][j]);
            
            // 计算并添加到对应宫块的集合
            // 宫块索引的计算:i/3*3 + j/3
            // - i/3 将行分成三组(0-2, 3-5, 6-8)
            // - 乘以3得到宫块行的起始索引(0, 3, 6)
            // - j/3 将列分成三组(0-2, 3-5, 6-8)作为偏移
            // 最终得到0-8共9个宫块索引
            int block_index = i / 3 * 3 + j / 3;
            s3[block_index].insert(a[i][j]);
        }
    }

    // 检查所有约束条件
    for (int i = 0; i < 9; i++) {
        // 检查第 i 行是否包含9个不同数字(即无重复)
        // 检查第 i 列是否包含9个不同数字
        // 检查第 i 个宫块是否包含9个不同数字
        // 任何一个集合的大小不是9,说明有重复数字或数字缺失
        if (s1[i].size() != 9 || s2[i].size() != 9 || s3[i].size() != 9) {
            cout << "NO";  // 不满足数独规则
            return 0;     // 立即退出程序
        }
    }
    
    cout << "YES";  // 所有检查都通过,输出YES
    return 0;
}

解析实现原理:

数据结构设计:

使用set数据结构自动处理重复值,因为集合不能包含重复元素

三个集合数组分别跟踪行、列和宫块中出现的数字

宫块索引计算:

i / 3 * 3 + j / 3 是宫块计算的精髓

例如位置(2,2):2/3=0 → 0*3=0,2/3=0 → 0+0=0(左上宫块)

位置(4,5):4/3=1 → 1*3=3,5/3=1 → 3+1=4(中央宫块)

位置(8,8):8/3=2 → 2*3=6, 8/3=2 → 6+2=8(右下宫块)

验证逻辑:

有效的数独必须同时满足三个条件:

每行包含1-9的不重复数字

每列包含1-9的不重复数字

每个宫块包含1-9的不重复数字

集合大小==9 是核心检查条件(保证9个不重复数字)

效率分析:

时间复杂度:O(n²) 对于9x9数独是常数时间

空间复杂度:O(1) 使用固定大小的数组(共3*9个集合)

太强大了竟然还可以这么写!!!!!!!!!!

评论