第五周周报

这周打了两场oi赛制的比赛,做的都一塌糊涂,会在许多的小问题上出错,比如爆int,数组越界·,甚至有一个题目输出都会看错,还是之前补过的题,这两场比赛让我知道一定要注意细节。 

题目:P8661 [蓝桥杯 2018 省 B] 日志统计

题目描述

小明维护着一个程序员论坛。现在他收集了一份“点赞”日志,日志共有 �N 行。其中每一行的格式是 ts id,表示在 ��ts 时刻编号 ��id 的帖子收到一个“赞”。

现在小明想统计有哪些帖子曾经是“热帖”。如果一个帖子曾在任意一个长度为 �D 的时间段内收到不少于 �K 个赞,小明就认为这个帖子曾是“热帖”。

具体来说,如果存在某个时刻 �T 满足该帖在 [�,�+�)[T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 �K 个赞,该帖就曾是“热帖”。

给定日志,请你帮助小明统计出所有曾是“热帖”的帖子编号。

输入格式

第一行包含三个整数 �N、�D 和 �K。

以下 �N 行每行一条日志,包含两个整数 ��ts 和 ��id。

输出格式

按从小到大的顺序输出热帖 ��id。每个 ��id 一行。

输入输出样例

输入 #1复制

7 10 2 
0 1 
0 10   
10 10 
10 1 
9 1
100 3 
100 3  

输出 #1复制


3  

说明/提示

对于 50%50% 的数据,1≤�≤�≤10001≤K≤N≤1000。

对于 100%100% 的数据,1≤�≤�≤1051≤K≤N≤105,0≤��,��≤1050≤id,ts≤105。

时限 1 秒, 256M。蓝桥杯 2018 年第九届省赛

解析:

本题我使用的是deque容器(双端数组,使用它可以同时取/删除头尾元素方便判断在一段时间内点赞数是否超过了k,并且方便我们移动需要判断的区域;

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
deque<int> q[100005];//下标表示ID,其中存每个ID的被点赞时刻
signed main(){
    int n,d,k;
    int zid=0;//记录最大下标
    cin>>n>>d>>k;
    for(int i=0;i<n;i++){
        int t,id;
        cin>>t>>id;
        zid=max(id,zid);
        q[id].push_back(t);//输入数据
    }
    for(int i=0;i<=zid;i++){
        if(!q[i].empty()){//对于每个存在的ID进行判断
            sort(q[i].begin(),q[i].end());//先将时间由小到大排列
            deque<int> f;//定义一个新的f存规定的一段时间内的点赞时间
            while(!q[i].empty()){//只要q不为空就一直判断下去
                f.push_back(q[i].front());//对f从小到大插入时间
                q[i].pop_front();
                if(f.back()-f.front()>=d){//当最开始记录的时间与现在的时间相差大于d,就需要将f的头部时间删除
                    f.pop_front();
                }
                if(f.size()>=k){//f的长度就代表赞的个数,大于k则是热评
                    cout<<i<<endl;//输出ID
                    break;
                }
            }
        }
    }
    return 0;
}

题目:P8649 [蓝桥杯 2017 省 B] k 倍区间

题目描述

给定一个长度为 �N 的数列,�1,�2,⋯��A1​,A2​,⋯AN​,如果其中一段连续的子序列 ��,��+1,⋯��(�≤�)Ai​,Ai+1​,⋯Aj​(i≤j) 之和是 �K 的倍数,我们就称这个区间 [�,�][i,j] 是 �K 倍区间。

你能求出数列中总共有多少个 �K 倍区间吗?

输入格式

第一行包含两个整数 �N 和 �K(1≤�,�≤105)(1≤N,K≤105)。

以下 �N 行每行包含一个整数 ��Ai​(1≤��≤105)(1≤Ai​≤105)。

输出格式

输出一个整数,代表 �K 倍区间的数目。

输入输出样例

输入 #1复制

5 2




5  

输出 #1复制

6

说明/提示

时限 2 秒, 256M。蓝桥杯 2017 年第八届

解析:

本题题意是计算有多少区间为k的倍数,这里有一个思路:记录该序列的前缀和,然后判断每个前缀和的模,与之前的前缀和有多少模相同,(模相同他们相减,得到的区间就是k倍区间),所以需要数组记录对k每个模的个数。需要特别注意前缀和可能会超过int 所以要用long long;

代码:

#include<bits/stdc++.h>
using namespace std;
    long long num[100005];
int main(){
    long long n,k,ans=0;
    cin>>n>>k;
    long long m[101000]={0};
    long long a;
    for(int i=1;i<=n;i++){
        cin>>a;
        num[i]=num[i-1]+a;
    }
    for(int i=0;i<=n;i++){//从0开始num[0]=0,前缀和本身可能就可以是k的倍数,这样就可以判断这种情况;
        ans+=m[num[i]%k]++;
    }
    cout<<ans<<endl;
    return 0;
}

题目:P8630 [蓝桥杯 2015 国 B] 密文搜索

题目描述

福尔摩斯从 X 星收到一份资料,全部是小写字母组成。

他的助手提供了另一份资料:许多长度为 88 的密码列表。

福尔摩斯发现,这些密码是被打乱后隐藏在先前那份资料中的。

请你编写一个程序,从第一份资料中搜索可能隐藏密码的位置。要考虑密码的所有排列可能性。

输入格式

输入第一行:一个字符串 �s,全部由小写字母组成,长度小于 1024×10241024×1024。

紧接着一行是一个整数 �,n, 表示以下有 �n 行密码,1≤�≤10001≤n≤1000。

紧接着是 �n 行字符串,都是小写字母组成,长度都为 88。

输出格式

一个整数,表示每行密码的所有排列在 �s 中匹配次数的总和。

输入输出样例

输入 #1复制

aaaabbbbaabbcccc
2
aaaabbbb
abcabccc

输出 #1复制

4

说明/提示

第一个密码匹配了 33 次,第二个密码匹配了 11 次,一共 44 次。

时限 3 秒, 512M。蓝桥杯 2015 年第六届国赛

解析:

本题因为密码是打乱放入的所以只要依次判断字符串中8个字符中是否有和密码一样的字符且其个数与密码一样,有的话就将匹配次数加1;记录每种字母出现的个数可以使用该字母-‘a'作为下标数组大小就是26.具体操作见代码。

代码:

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

int main(){
    string s;
    cin>>s;
    int n,ans=0;
    cin>>n;
    while(n--){
        string a;
        cin>>a;
        int ha[26]={0};//存密码每个字母和个数
        int h[26]={0};//存8个字符中的各个字母和它的个数
        for(int i=0;i<8;i++){
            ha[a[i]-'a']++;//每种字母个数
        }
        for(int i=0;i<s.size();i++){//对字符串s进行遍历
            if(i<7){//还没存到8个时
                h[s[i]-'a']++;
            }
            else{
                h[s[i]-'a']++;//加上当前字符
                int c=1;
                for(int j=0;j<26;j++){
                    if(ha[j]==h[j])continue;
                    else{
                        c=0;//只要有一个不相等就不是密码
                        break;
                    }
                }
                if(c!=0) ans++;//判断标记
                h[s[i-7]-'a']--;//8个字符中最前面的字符个数减一;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

题目:P8662 [蓝桥杯 2018 省 AB] 全球变暖

题目描述

你有一张某海域 �×�N×N 像素的照片,. 表示海洋、 # 表示陆地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......

其中 "上下左右" 四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 22 座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

输入格式

第一行包含一个整数 �N。(1≤�≤1000)(1≤N≤1000)。

以下 �N 行 �N 列代表一张海域照片。

照片保证第 11 行、第 11 列、第 �N 行、第 �N 列的像素都是海洋。

输出格式

一个整数表示答案。

输入输出样例

输入 #1复制

7
.......
.##....
.##....
....##.
..####.
...###.
.......  

输出 #1复制

1

说明/提示

时限 1 秒, 256M。蓝桥杯 2018 年第九届省赛

解析:

这题是一道非常非常经典的dfs算法的题目,我之前补过这道题结果训练的时候还是写错了,它需要求的是会有多少岛被淹没,我结果求成了还剩多少个岛。具体操作看代码。

代码:

#include<bits/stdc++.h>
using namespace std;
char a[10005][10005];
int lg=0;
int ant=0,ans=0;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};//方便判断上下左右
void dfs(int x,int y){
    if(lg==1){//判断是否会被淹没,且对一座岛只判断一次
        int c=0;
        for(int i=0;i<4;i++){
            if(a[x+dx[i]][y+dy[i]]!='.') c++;
        }
        if(c==4){
            ant++;
            lg=0;
        }
    }
    a[x][y]='*';//对判断过的点进行标记
    for(int i=0;i<4;i++){
        if(a[x+dx[i]][y+dy[i]]=='#'){//该岛的其他相邻的陆地进行判断标记
            int xx=x+dx[i],yy=y+dy[i];
            dfs(xx,yy);
        }
        else continue;
    }
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>a[i][j];
        }
    }
    for(int i=1;i<n-1;i++){
        for(int j=1;j<n-1;j++){
            if(a[i][j]=='#'){
                ans++;//遇到了#没被淹前就一定是岛;
                lg=1;
                dfs(i,j);
            }
        }
    }
    cout<<ans-ant<<endl;//起初岛的数量减去还剩岛的数量就是被淹没的岛的数量
    return 0;
}

题目:P8665 [蓝桥杯 2018 省 A] 航班时间

题目描述

小 h 前往美国参加了蓝桥杯国际赛。小 h 的女朋友发现小 h 上午十点出发,上午十二点到达美国,于是感叹到“现在飞机飞得真快,两小时就能到美国了”。

小 h 对超音速飞行感到十分恐惧。仔细观察后发现飞机的起降时间都是当地时间。由于北京和美国东部有 1212 小时时差,故飞机总共需要 1414 小时的飞行时间。

不久后小 h 的女朋友去中东交换。小 h 并不知道中东与北京的时差。但是小 h 得到了女朋友来回航班的起降时间。小 h 想知道女朋友的航班飞行时间是多少。

对于一个可能跨时区的航班,给定来回程的起降时间。假设飞机来回飞行时间相同,求飞机的飞行时间。

输入格式

从标准输入读入数据。

一个输入包含多组数据。

输入第一行为一个正整数 �T,表示输入数据组数。

每组数据包含两行,第一行为去程的起降时间,第二行为回程的起降时间。

起降时间的格式如下

h1:m1:s1 h2:m2:s2

h1:m1:s1 h3:m3:s3 (+1)

h1:m1:s1 h4:m4:s4 (+2)

表示该航班在当地时间 h1 时 m1 分 s1 秒起飞,

第一种格式表示在当地时间 当日 h2 时 m2 分 s2 秒降落

第二种格式表示在当地时间 次日 h3 时 m3 分 s3 秒降落。

第三种格式表示在当地时间 第三天 h4 时 m4 分 s4 秒降落。

对于此题目中的所有以 h:m:s 形式给出的时间, 保证(0≤ℎ≤230≤h≤23,0≤�,�≤590≤m,s≤59).

输出格式

输出到标准输出。

对于每一组数据输出一行一个时间 hh:mm:ss,表示飞行时间为 hh 小时 mm 分 ss 秒。

注意,当时间为一位数时,要补齐前导零。如三小时四分五秒应写为 03:04:05

输入输出样例

输入 #1复制

3
17:48:19 21:57:24
11:05:18 15:14:23
17:21:07 00:31:46 (+1)
23:02:41 16:13:20 (+1)
10:19:19 20:41:24
22:19:04 16:41:09 (+1)

输出 #1复制

04:09:05
12:10:39
14:22:05

说明/提示

保证输入时间合法,飞行时间不超过 2424 小时。

解析:

去程起降时间=时差+飞机飞行时间

回程起降时间=飞行时间-时差,可以得到飞机飞行时间就是两个时间相加再除以2;还需注意对数据的读入(+1)的处理,如果数字结束后是空格代表还有第几天,回车的话就没有。对时间的加减可以全部转化成秒计算,最后再化成指定格式输出。

代码:

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

int main(){
    int n;
    cin>>n;

    while(n--){
        int h1,h2,m2,m1,s1,s2,day;
        string s;
        scanf("%d:%d:%d",&h1,&m1,&s1);
        scanf("%d:%d:%d",&h2,&m2,&s2);
        if(getchar()==' '){
            scanf("(+%d)",&day);
        }
        else{
            day=0;
        }
        int sum1= day*24*3600+h2*3600+m2*60+s2-(3600*h1+m1*60+s1);
        scanf("%d:%d:%d",&h1,&m1,&s1);
        scanf("%d:%d:%d",&h2,&m2,&s2);
        if(getchar()==' '){
            scanf("(+%d)",&day);
        }
        else{
            day=0;
        }        
        int sum2= day*24*3600+h2*3600+m2*60+s2-(3600*h1+m1*60+s1);
        int sum=(sum1+sum2)/2;
        s1=sum%60;
        m1=(sum/60)%60;
        h1=sum/3600;
        printf("%.02d:%.02d:%.02d\n",h1,m1,s1);       
    }
    return 0;
}

题目:B3911 [语言月赛 202312] 铅球杯

题目描述

蓝边铅球组织了“铅球杯”数据标注大赛。为了实现 Au 大满贯的宏大征途,LeAuingZ 报名参加了比赛。

蓝边铅球给出了 �N 个 int 类型变量的名字及其值,并要求 LeAuingZ 对 �k 句话进行数据标注。每句话由大小写英文字母、空格、半角逗号、半角句号和 {} 组成。在 {} 之间的,为 �N 个变量名中的一个,LeAuingZ 需要将每一句话中全部的 {变量名} 替换为变量的值并输出。

例如,有 �=3,�=4a=3,b=4,对于句子 We know a is {a}, b is {b}.,替换后将得到 We know a is 3, b is 4.

LeAuingZ 觉得这个任务很无聊,决定编写一个程序来快速获得 Au。

输入格式

输入共 �+�+1N+k+1 行。

输入的第一行为两个整数 �,�N,k。

接下来 �N 行,每行一个小写英文字符串、一个整数,分别代表变量名和变量的值。

接下来 �k 行,每行一个需要标注的句子。

输出格式

输出 �k 行,每行一个标注好的句子。

输入输出样例

输入 #1复制

5 2
abc 1
a 2
b 3
c 4
d 5
We have {a} apples.
We {d}onot have pencils.

输出 #1复制

We have 2 apples.
We 5onot have pencils.

说明/提示

  • 对于 20%20% 的测试数据,�=1k=1。

  • 对于另外 30%30% 的测试数据,1≤�≤261≤N≤26,变量名长度均为 11。

  • 对于 100%100% 的测试数据,1≤�≤50001≤N≤5000,1≤�≤201≤k≤20。变量名仅含英文小写字母,变量名长度不超过 2020,变量的值在 int 范围内,标注前句子长度不超过 5×1045×104,保证 {} 成对合法出现。每句话由大小写英文字母、空格、半角逗号、半角句号和 {} 组成。

解析:

因为需要判断{变量名}所以在输入的时候就将需要判断的变量加上大括号,方便之后使用replace函数替换,//需要注意读取回车这已经是好多次因为回车符犯得错了。注意replace函数的使用。

代码:

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

int main(){
    int n,k;
    string a[10004],b[10004];
    cin>>n>>k;
    for(int i=0;i<n;i++){
        string s;
        cin>>a[i]>>b[i];
        s="{";
        s=s+a[i]+"}";
        a[i]=s;
    }
    getchar();//前面输入了其他东西注意注意读回车符
    while(k--){
        string s;
        getline(cin,s);
        for(int i=0;i<n;i++){
            int x=s.find(a[i]);
            while(x!=-1){
                s=s.replace(x,a[i].length(),b[i]);
                x=s.find(a[i]);
            }
        } 
        cout<<s<<endl;
    }
    return 0;
}   

题目:P9241 [蓝桥杯 2023 省 B] 飞机降落

题目描述

�N 架飞机准备降落到某个只有一条跑道的机场。其中第 �i 架飞机在 ��Ti​ 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 ��Di​ 个单位时间,即它最早可以于 ��Ti​ 时刻开始降落,最晩可以于 ��+��Ti​+Di​ 时刻开始降落。降落过程需要 ��Li​ 个单位时间。

一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。

请你判断 �N 架飞机是否可以全部安全降落。

输入格式

输入包含多组数据。

第一行包含一个整数 �T,代表测试数据的组数。

对于每组数据,第一行包含一个整数 �N。

以下 �N 行,每行包含三个整数 ��,��,��Ti​,Di​,Li​。

输出格式

对于每组数据,输出 YES 或者 NO,代表是否可以全部安全降落。

输入输出样例

输入 #1复制

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

输出 #1复制

YES
NO

说明/提示

【样例说明】

对于第一组数据,可以安排第 3 架飞机于 0 时刻开始降落,20 时刻完成降落。安排第 2 架飞机于 20 时刻开始降落,30 时刻完成降落。安排第 1 架飞机于 30 时刻开始降落,40 时刻完成降落。

对于第二组数据,无论如何安排,都会有飞机不能及时降落。

【评测用例规模与约定】

对于 30%30% 的数据,�≤2N≤2。

对于 100%100% 的数据,1≤�≤101≤T≤10,1≤�≤101≤N≤10,0≤��,��,��≤1050≤Ti​,Di​,Li​≤105。

蓝桥杯 2023 省赛 B 组 D 题。

解析:

可以看到N的最大值为10,所以可以使用dfs来枚举每一种飞机起飞情况,只要存在合理的一种就输出‘YES’;用结构体记录每个飞机的t,d,

代码:

#include<bits/stdc++.h>
using namespace std;
struct fly{
    int t;
    int d;
    int l;
}a[11];
int n,lg=0;
int vis[11];//标记飞机是否被判断过
void dfs(int x,int time){
    if(x>n){
        lg=1;//此时可以完全起飞,改变标记
        return;
    }
    for(int i=0;i<n;i++){
        if(vis[i]||time>(a[i].d+a[i].t)) continue;//被判断过的飞机和不能起飞的飞机跳过判断
        vis[i]=1;//标记判断
        dfs(x+1,max(time,a[i].t)+a[i].l);//改变下一个判断时间/时间为开始降落时间+降落需要的时间,与time,和飞机起飞最早时间取最大值,是因为飞机可能还没有到,这上一架飞机已经降落完毕。找到最大值加上此飞机降落时间就是新time;
            vis[i]=0;//取消标记,回溯
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>a[i].t>>a[i].d>>a[i].l;
        }
        memset(vis,0,11);
        lg=0;//对每一组数据判断之前都要重新标记
        dfs(1,0);//第一架飞机,最开始时间为0;
        if(lg==1){
            cout<<"YES"<<endl;
        }
        else{
            cout<<"NO"<<endl;
        }    
    }
}

题目:P9242 [蓝桥杯 2023 省 B] 接龙数列

题目描述

对于一个长度为 �K 的整数数列:�1,�2,…,��A1​,A2​,…,AK​,我们称之为接龙数列当且仅当 ��Ai​ 的首位数字恰好等于 ��−1Ai−1​ 的末位数字(2≤�≤�2≤i≤K)。

例如 12,23,35,56,61,1112,23,35,56,61,11 是接龙数列;12,23,34,5612,23,34,56 不是接龙数列,因为 5656 的首位数字不等于 3434 的末位数字。所有长度为 11 的整数数列都是接龙数列。

现在给定一个长度为 �N 的数列 �1,�2,…,��A1​,A2​,…,AN​,请你计算最少从中删除多少 个数,可以使剩下的序列是接龙序列?

输入格式

第一行包含一个整数 �N。

第二行包含 �N 个整数 �1,�2,…,��A1​,A2​,…,AN​。

输出格式

一个整数代表答案。

输入输出样例

输入 #1复制

5
11 121 22 12 2023

输出 #1复制

1

说明/提示

【样例说明】

删除 2222,剩余 11,121,12,202311,121,12,2023 是接龙数列。

【评测用例规模与约定】

对于 20%20% 的数据,1≤�≤201≤N≤20。

对于 50%50% 的数据,1≤�≤1041≤N≤104。

对于 100%100% 的数据,1≤�≤1051≤N≤105,1≤��≤1091≤Ai​≤109。所有 ��Ai​ 保证不包含前导 0。

蓝桥杯 2023 省赛 B 组 E 题。

解析:

本题需要找出最少删除的个数,使其成为接龙序列,只需要找到最大接龙序列减去原序列就能得出答案,因为就只需要看首尾所以就只存手尾的数字是几就可以了这里数据是很大的所以这里就使用动态规划 状态转移方程:dp[r[i]]=max(dp[l[i]]+1,dp[r[i]]); 到这个数为止的最长序列是以连接上一个,还是以r结尾为最长 ,改变以r为结尾的序列长度。最后再遍历一遍谁结尾为最长的序列,n减去他得到答案;//动态规划还是需要找到其状态转移方程就简单了;

代码:

#include<bits/stdc++.h>
using namespace std;
int l[100005],r[100005];
int dp[10];
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        string s;
        cin>>s;
        l[i]=(s[0]-'0');
        r[i]=(s[s.size()-1]-'0');
    }
    for(int i=0;i<n;i++){
        dp[r[i]]=max(dp[l[i]]+1,dp[r[i]]);
    }
    int ans=-1;
    for(int i=0;i<10;i++){
        if(dp[i]>ans) ans=dp[i];
    }
    cout<<n-ans<<endl;
    return 0;
}

题目: P8674 [蓝桥杯 2018 国 B] 调手表

题目描述

小明买了块高端大气上档次的电子手表,他正准备调时间呢。

在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 �n 分钟。

大家都知道,手表只有一个按钮可以把当前的数加一。在调分钟的时候,如果当前显示的数是 00,那么按一下按钮就会变成 11,再按一次变成 22。如果当前的数是 �−1n−1,按一次后会变成 00。

作为强迫症患者,小明一定要把手表的时间调对。如果手表上的时间比当前时间多 11,则要按 �−1n−1 次加一按钮才能调回正确时间。

小明想,如果手表可以再添加一个按钮,表示把当前的数加 �k 该多好啊……

他想知道,如果有了这个 +�+k 按钮,按照最优策略按键,从任意一个分钟数调到另外任意一个分钟数最多要按多少次。

注意,按 +�+k 按钮时,如果加 �k 后数字超过 �−1,n−1, 则会对 �n 取模。

比如,�=10,�=6n=10,k=6 的时候,假设当前时间是 00,连按 22 次 +�+k 按钮,则调为 22。

输入格式

一行两个整数 �,�n,k,意义如题。

输出格式

一行一个整数。表示:按照最优策略按键,从一个时间调到另一个时间最多要按多少次。

输入输出样例

输入 #1复制

5 3

输出 #1复制

2

说明/提示

【样例解释】

如果时间正确则按 00 次。否则要按的次数和操作系列之间的关系如下:

  1. +1

  2. +1, +1

  3. +3

  4. +3, +1

【数据约定】

对于 30%30% 的数据 0<�<�≤50<k<n≤5。

对于 60%60% 的数据 0<�<�≤1000<k<n≤100。

对于 100%100% 的数据 0<�<�≤1050<k<n≤105。

时限 3 秒, 256M。蓝桥杯 2018 年第九届国赛

解析:

之前想的是对每个数取模,模加上它与k的倍数就是最优方案,但是没有考虑到n很大,k也为n的一半以上的情况这种想法就是错误的比如,n=10,k=7最快到4按的次数就不是4,而是按两次k就可以了。这时候就需要bfs判断每个数第一次出现按多少次到n-1所有之前的数判断完了,找出其中最大的按键次数就是按照最优策略按键,从一个时间调到另一个时间最多要按多少次;

代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,ans=0;
int dis[100005]={0};
void bfs(){
    queue<int > q[2];
    q[0].push(0);//记录数
    q[1].push(0);//记录该数需要调的次数
    while(!q[0].empty()){
        int x=q[0].front(),s=q[1].front();
        q[0].pop(),q[1].pop();
        if(dis[x]) continue;//之前遍历过了,跳过
        dis[x]=s;
        q[0].push((x+1)%n),q[1].push(s+1);//以此点加一
        q[0].push((x+k)%n),q[1].push(s+1);//以此点加k;
    }
}

int main(){
    cin>>n>>k;
    bfs();
    for(int i=1;i<n;i++){
        ans=max(ans,dis[i]);
    }
    cout<<ans<<endl;
    return 0;
}

题目:P9232 [蓝桥杯 2023 省 A] 更小的数

题目描述

image

小蓝有一个长度均为 �n 且仅由数字字符 0∼90∼9 组成的字符串,下标从 00 到 �−1n−1,你可以将其视作是一个具有 �n 位的十进制数字 ���num,小蓝可以从 ���num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 ������numnew​ 满足条件 ������<���numnew​<num,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 ���num 中的位置不完全相同我们就视作是不同的方案。

注意,我们允许前导零的存在,即数字的最高位可以是 00,这是合法的。

输入格式

输入一行包含一个长度为 �n 的字符串表示 ���num(仅包含数字字符 0∼90∼9),从左至右下标依次为 0∼�−10∼n−1。

输出格式

输出一行包含一个整数表示答案。

输入输出样例

输入 #1复制

210102

输出 #1复制

8

说明/提示

【样例说明】

一共有 88 种不同的方案:

  1. 所选择的子串下标为 0∼10∼1,反转后的 ������=120102<210102numnew​=120102<210102;

  2. 所选择的子串下标为 0∼20∼2,反转后的 ������=012102<210102numnew​=012102<210102;

  3. 所选择的子串下标为 0∼30∼3,反转后的 ������=101202<210102numnew​=101202<210102;

  4. 所选择的子串下标为 0∼40∼4,反转后的 ������=010122<210102numnew​=010122<210102;

  5. 所选择的子串下标为 0∼50∼5,反转后的 ������=201012<210102numnew​=201012<210102;

  6. 所选择的子串下标为 1∼21∼2,反转后的 ������=201102<210102numnew​=201102<210102;

  7. 所选择的子串下标为 1∼41∼4,反转后的 ������=201012<210102numnew​=201012<210102;

  8. 所选择的子串下标为 3∼43∼4,反转后的 ������=210012<210102numnew​=210012<210102。

【评测用例规模与约定】

对于 20%20% 的评测用例,1≤�≤1001≤n≤100;

对于 40%40% 的评测用例,1≤�≤10001≤n≤1000;

对于所有评测用例,1≤�≤50001≤n≤5000。

解析:

题目意思翻转一段子串让新串小于旧串,求求有多少这样的串,其实只要知道翻转后的子串小于翻转前的子串得到的num就比原来的小,遍历每一个可能。

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s;
    int ans=0;
    cin>>s;
    for(int i=0;i<s.size()-1;i++){//开头从前往后
        for(int j=i+1;j<s.size();j++){//结尾从后往前
            if(s[i]<s[j]) continue;//前比后小,翻转变大不满足
            else if(s[i]==s[j]){//相等需要继续判断这个区间字符
                for(int t=i+1,k=j-1;t<k;t++,k--){
                    if(s[t]==s[k]) continue;
                    if(s[t]<s[k]){
                         break;
                    }
                    else{//只要前比后大就加
                        ans++;
                        break;
                    }
                }
            }
            else{
                ans++;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

  • 26
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值