2022暑假每日一题笔记(一)

T1--4268. 性感素数

“性感素数 ”是指形如 (p,p+6) 这样的一对素数。
之所以叫这个名字,是因为拉丁语管“六”叫“sex”(即英语的“性感”)。
现给定一个整数,请你判断其是否为一个性感素数。

输入格式
输入在一行中给出一个正整数 N。

输出格式
若 N 是一个性感素数,则在一行中输出 Yes,并在第二行输出与 N 配对的另一个性感素数(若这样的数不唯一,输出较小的那个)。
若 N 不是性感素数,则在一行中输出 No,然后在第二行输出大于 N 的最小性感素数。

数据范围
1≤N≤10^8
输入样例147
输出样例1:
Yes
41
输入样例221
输出样例2:
No
23

简单的判断素数的题目。

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

bool is_prime(int x){
    if (x < 2) return false;
    for (int i = 2;i <= x/i;i ++){
        if (x % i == 0) return false;
    }
    return true;
}

int main(){
    int n;
    cin >> n;
    
    bool flag = false;
    if (is_prime(n)){
        if (is_prime(n-6)){
            flag = true;
            cout << "Yes\n" << n-6 << '\n';
        }
        else if (is_prime(n+6)){
            flag = true;
            cout << "Yes\n" << n+6 << '\n';
        }
    }
    
    if (!flag){
        for (int i = n-5;i <= 1e8+6;i ++){
            if (is_prime(i) && is_prime(i+6)){
                cout << "No\n" << (i < n ? i+6 : i) << '\n';
                break;
            }
        }
    }

    return 0;
}

T2--4269. 校庆

2019 年浙江大学将要庆祝成立 122 周年。
为了准备校庆,校友会收集了所有校友的身份证号。
现在需要请你编写程序,根据来参加校庆的所有人士的身份证号,统计来了多少校友。

输入格式
输入在第一行给出正整数 N。
随后 N 行,每行给出一位校友的身份证号(18 位由数字和大写字母 X 组成的字符串)。题目保证身份证号不重复。
随后给出前来参加校庆的所有人士的信息:
首先是一个正整数 M。
随后 M 行,每行给出一位人士的身份证号。题目保证身份证号不重复。

输出格式
首先在第一行输出参加校庆的校友的人数。
然后在第二行输出最年长的校友的身份证号 —— 注意身份证第 714 位给出的是 yyyymmdd 格式的生日。
如果没有校友来,则在第二行输出最年长的来宾的身份证号。题目保证这样的校友或来宾必是唯一的。

数据范围
1≤N,M≤10^5
输入样例:
5
372928196906118710
610481197806202213
440684198612150417
13072819571002001X
150702193604190912
6
530125197901260019
150702193604190912
220221196701020034
610481197806202213
440684198612150417
370205198709275042
输出样例:
3
150702193604190912
#include <bits/stdc++.h>
using namespace std;

bool cmp(string a,string b){
    return a.substr(6,8) > b.substr(6,8);
}

int main(){
    int n;
    cin >> n;
    
    string str;
    unordered_set<string> s;
    for (int i = 0;i < n;i ++){
        cin >> str;
        s.insert(str);
    }
    
    int m,cnt = 0;
    string ans1,ans2;
    
    cin >> m;
    for (int i = 0;i < m;i ++){
        cin >> str;
        if (s.find(str) != s.end()){
            if (ans1.empty()) ans1 = str;
            if (cmp(ans1,str)) ans1 = str;
            cnt ++;
        }
        
        if (!i) ans2 = str;
        if (i && cmp(ans2,str)) ans2 = str;
    }
    
    cout << cnt << '\n';
    if (!cnt) cout << ans2;
    else cout << ans1;
    return 0;
}

T3--4273. 链表合并

给定两个单链表 L1=a1→a2→…→an−1→an 和 L2=b1→b2→…→bm−1→bm,满足:n≥2m。
你的任务是将较短的那个链表逆序,然后将之并入较长的链表,得到形如 a1→a2→bm→a3→a4→bm−1… 的结果。
例如给定两个链表分别为 6712345,你应该输出 1273465。

补充
本题中可能包含不在两个单链表中的节点,这些节点无需考虑。

输入格式
输入首先在第一行中给出两个链表 L1 和 L2 的头结点的地址,以及正整数 N,即给定的结点总数。
一个结点的地址是一个 5 位数的非负整数(可能包含前导 0),空地址 NULL 用 −1 表示。
随后 N 行,每行按以下格式给出一个结点的信息:
Address Data Next
其中 Address 是结点的地址,Data 是不超过 10^5 的正整数,Next 是下一个结点的地址。
题目保证没有空链表,并且较长的链表至少是较短链表的两倍长。

输出格式
按顺序输出结果链表,每个结点占一行,格式与输入相同。

数据范围
1≤N≤10^5
输入样例:
00100 01000 7
02233 2 34891
00100 6 00001
34891 3 10086
01000 1 02233
00033 5 -1
10086 4 00033
00001 7 -1
输出样例:
01000 1 02233
02233 2 00001
00001 7 34891
34891 3 10086
10086 4 00100
00100 6 00033
00033 5 -1

不太会,参考自y总。

对于非面试以外情景的链表题,不推荐写链表,最后用数组模拟。

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second

const int N = 1e5+5;
int val[N],ne[N];
int h1,h2,n;

int main(){
    cin >> h1 >> h2 >> n;
    
    int addr,value,next;
    for (int i = 0;i < n;i ++){
        cin >> addr >> value >> next;
        val[addr] = value,ne[addr] = next;
    }
    
    vector<PII> a,b; // 数组模拟链表,每个结点存放value和next
    for (int i = h1;i != -1;i = ne[i]) a.push_back({i,val[i]});
    for (int i = h2;i != -1;i = ne[i]) b.push_back({i,val[i]});
    
    if (a.size() < b.size()) swap(a,b);
    
    vector<PII> c; // 合并后的链表
    for (int i = 0,j = b.size()-1;i < a.size();i += 2,j --){
        c.push_back(a[i]);
        if (i + 1 < a.size()) c.push_back(a[i+1]);
        if (j >= 0) c.push_back(b[j]);
    }
    
    for (int i = 0;i < c.size();i ++){
        printf("%05d %d ",c[i].x,c[i].y);
        if (i < c.size()-1) printf("%05d\n",c[i+1].x);
        else cout << -1;
    }
    return 0;
}

T4--4274. 后缀表达式

给定一个二叉表达式树,请你输出相应的后缀表达式,要求使用括号反映运算符的优先级。

输入格式
第一行包含整数 N,表示节点数量。节点编号 1∼N。
接下来 N 行,每行给出一个节点的信息(第 i 行对应第 i 个节点),格式为:
data left_child right_child
其中,data 是一个不超过 10 个字符的字符串,left_child 和 right_child 分别是该节点的左右子节点的编号。
没有子节点(即 NULL),则用 −1 表示。
下面两图分别对应给出的两个样例。

输出格式
在一行中输出答案,表达式符号之间不得有空格。

数据范围
1≤N≤20
输入样例1:
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
输出样例1:
(((a)(b)+)((c)(-(d))*)*)
输入样例2:
8
2.35 -1 -1
* 6 1
- -1 4
% 7 8
+ 2 3
a -1 -1
str -1 -1
871 -1 -1
输出样例2:
(((a)(2.35)*)(-((str)(871)%))+)

参考y总代码。

给定表达式树,将其转化为后缀表达式,也就是对树做后序遍历。

如果同时有左右孩子,递归左边、右边,然后输出当前值;
如果只有右孩子,说明是单操作符(负号),先输出当前值,再递归右边;
如果没有孩子,输出当前值。

#include <bits/stdc++.h>
using namespace std;
const int N = 25;
string v[N];
int l[N],r[N];
bool st[N];

void dfs(int u){
    cout << "(";
    if (l[u] != -1 && r[u] != -1){ // 两个孩子都有
        dfs(l[u]);
        dfs(r[u]);
        cout << v[u];
    }
    else if (l[u] == -1 && r[u] == -1){
        cout << v[u];
    }
    else{
        cout << v[u];
        dfs(r[u]);
    }
    cout << ")";
}

int main(){
    int n;
    cin >> n;
    
    for (int i = 1;i <= n;i ++){
        cin >> v[i] >> l[i] >> r[i];
        if (l[i] != -1) st[l[i]] = true; // 记录左孩子l[i]具有父节点
        if (r[i] != -1) st[r[i]] = true; // 记录右孩子r[i]具有父节点
    }
    
    int root = -1; // 通过st数组找根
    for (int i = 1;i <= n;i ++){
        if (!st[i]) root = i; // st[i] == false 表示没有父节点
    }
    
    dfs(root);
    return 0;
}

T5--4275. Dijkstra序列

Dijkstra 算法是非常著名的贪心算法之一。
它用于解决单源最短路径问题,即指定一个特定源顶点,求该顶点到给定图的所有其他顶点的最短路径。
它由计算机科学家 Edsger W. Dijkstra 于 1956 年构思并在三年后出版。
在该算法中,我们需要不断维护一个包含最短路径树中顶点的集合。
在每一步中,我们找到一个尚未在集合内且与源顶点距离最小的顶点,并将其收于集合中。
因此,通过 Dijkstra 算法,我们可以逐步生成一个有序的顶点序列,我们称之为 Dijkstra 序列。
对于一个给定的图,可能有多个 Dijkstra 序列。
例如,{5,1,3,4,2} 和 {5,3,1,2,4} 都是给定图的 Dijkstra 序列。
注意,序列中的第一个顶点即为指定的特定源顶点。
你的任务是检查给定的序列是否是 Dijkstra 序列。

输入格式
第一行包含两个整数 N 和 M,表示图中点和边的数量。
点的编号 1∼N。
接下来 M 行,每行包含三个整数 a,b,c,表示点 a 和点 b 之间存在一条无向边,长度为 c。
再一行包含整数 K,表示需要判断的序列个数。
接下来 K 行,每行包含一个 1∼N 的排列,表示一个给定序列。

输出格式
共 K 行,第 i 行输出第 K 个序列的判断,如果序列是 Dijkstra 序列则输出 Yes,否则输出 No。

数据范围
1≤N≤1000,
1≤M≤10^5,
1≤a,b≤N,
1≤c≤100,
1≤K≤100,
保证给定无向图是连通图,
保证无重边和自环(官网没提,但是经实测,官网数据符合此条件)。

输入样例:
5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4
输出样例:
Yes
Yes
Yes
No

思路:Dijkstra 算法得到的点序列一定满足到源点的距离dist是递增的,满足dist递增的序列也就是Dijkstra 序列。

如何判断一个序列是不是Dijkstra 序列?

1.根据上面的结论,看看dist距离是否满足递增;

2.在执行Dijkstra 算法时,从剩余点中判断给定序列的当前点是否满足到源点集合距离最短,若不满足则它不是Dijkstra 序列。

#include <bits/stdc++.h>
using namespace std;
const int N = 1005,INF = 0x3f3f3f3f;
int q[N],d[N],g[N][N];
int n,m;
bool st[N];

bool dijkstra(){
    memset(d,0x3f,sizeof d);
    memset(st,0,sizeof st); // 记得d数组和st数组都要重置
    d[q[1]] = 0;
    
    for (int i = 1;i <= n;i ++){
        int t = q[i];
        for (int j = 1;j <= n;j ++)
            if (!st[j] && d[t] > d[j])
                return false;
        st[t] = true;
        for (int j = 1;j <= n;j ++)
            d[j] = min(d[j],d[t] + g[t][j]);
    }
    return true;
}

int main(){
    cin >> n >> m;
    
    int a,b,c;
    memset(g,0x3f,sizeof g); // 记得邻接矩阵初始化
    while (m --){
        scanf("%d %d %d",&a,&b,&c);
        g[a][b] = g[b][a] = c;
    }
    
    int k;
    cin >> k;
    while (k --){
        for (int i = 1;i <= n;i ++) scanf("%d",&q[i]);
        if (dijkstra()) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
posted @   grant_drew  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示