[模拟]Csp M3 A+B+C

A.[模拟]序列

题意

5.1期间大家都出去玩了,只有瑞神还在孜孜不倦的学习,瑞神想到了一个序列,这个序列长度为n,也就是一共有n个数,瑞神给自己出了一个问题:数列有几段? 段的定义是连续的相同的最长整数序列

样例

样例输入:

第一行一个数n (1<=n<=1000)表示序列元素的个数
第二行n个数 为该序列(1<=ai<=1000)

12
2 3 3 6 6 6 1 1 4 5 1 4 

样例输出:

一个整数 为数列的段数

8

思路

从头到尾遍历序列,并用一个变量tmp记录当前段中元素的大小 如果当前元素不等于tmp 则说明有一个新的段


总结

简单的模拟题


代码

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
#define MAXN 1010
using namespace std;
int ans=1;
int n;
int main()
{
    cin>>n;
    int tmp,x;
    cin>>tmp;
    for(int i=2;i<=n;i++)
    {
        cin>>x;
        if(x!=tmp)
        {
            ans++;
            tmp=x;
        }
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

B.[模拟]消消乐

题意

游戏在一个包含有n行m列的棋盘上进行,棋盘的每个格子都有一种颜色的棋子。当一行或一列 上有连续三个或更多的相同颜色的棋子时,这些棋子都被消除。当有多处可以被消除时,这些地方的棋子将同时被消除。一个棋子可能在某一行和某一列同时被消除。

样例

样例输入:

输入第一行包含两个整数n,m,表示行数和列数 接下来n行m列,每行中数字用空格隔开,每个数字代表这个位置的棋子的颜色。数字都大于0.

4 5 
2 2 3 1 2 
3 4 5 1 4 
2 3 2 1 3 
2 2 2 4 4

样例输出:

输出n行m列,每行中数字用空格隔开,输出消除之后的棋盘。(如果一个方格中的棋子被消除,则对应的方格输出0,否则输出棋子的颜色编号。)

2 2 3 0 2 
3 4 5 0 4 
2 3 2 0 3 
0 0 0 4 4


思路

按照题意进行模拟
进行两次遍历 第一次遍历 寻找所有能消除的横行 即如果color[i-1][j]==color[i][j]&&color[i][j]==color[i+1][j] 就说明这三个可以消除 用一个数组xc[i][j]表示该格子是否被消除
同理 第二次遍历寻找所有能消除的纵行 条件为color[i][j-1]==color[i][j]&&color[i][j]==color[i][j+1]


总结

也是一个简单的模拟题 只需要注意一下边界


代码

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
#define MAXN 1010
using namespace std;
int color[100][100];
int xc[100][100];
int n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)   cin>>color[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            //先判断行
            if(i-1<1||i+1>n)    continue;
            if(color[i-1][j]==color[i][j]&&color[i][j]==color[i+1][j])
            {
                xc[i-1][j]=1;
                xc[i][j]=1;
                xc[i+1][j]=1;
            }
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            //先判断行
            if(j-1<1||j+1>m)    continue;
            if(color[i][j-1]==color[i][j]&&color[i][j]==color[i][j+1])
            {
                xc[i][j-1]=1;
                xc[i][j]=1;
                xc[i][j+1]=1;
            }
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)   if(xc[i][j]==1) color[i][j]=0;

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)   
        {
            if(j!=m)    cout<<color[i][j]<<" ";
            else cout<<color[i][j];
        }
        cout<<endl;
    }
    system("pause");
    return 0;
}

C.[模拟]

题意

给定一个只含大写字母A,B的字符串 问这个字符串中有多少子串是delicious
delicious:该子串的每一个字符都属于一个长度大于1的回文子串中

样例

样例输入:

第一行为一个整数n(n<=3*10^5) 表示该字符串的长度
第二行为该字符串

5
AABBB

样例输出:

满足定义的子串数目

6

思路

1.在模测时很遗憾的没有AC(尽管前两题的简单程度让我有充足的时间想这道题 不过好像思路想歪了TAT) 于是写了一个60分的暴力 代码在下面贴着

暴力的思路就是枚举每一个子串,然后对这个子串求他的回文子串,并把出现在他回文子串的所有字符都用一个数组标记,遍历完后检查一下该子串的所有元素是否都已经被标记,如果是则说明该子串是delicious的,否则不是

2.然而上述复杂度爆炸的算法 必然是无法正确求解 10^5 数据的题目的(废话)
加入正确的思考(来源未知)之后呢 我们会发现:
一个长度为n的字符串 共有n-1 + n-2 + n-3 + ··· + 1=(n-1)*n/2个长度大于等于2的字符串
①.如果一个字符a[i] a[i]==a[i-1] 或 a[i]==a[i+1] 那么他肯定是存在于一个长度为2的回文子串中的 即满足条件
②.如果一个字符a[i] a[i]!=a[i+1] 且 a[i]!=a[i-1] 那么他肯定存在于一个长度为3的回文子串中 也满足条件

那么什么样的字符不满足条件呢?
位于某一连续序列的端点的字符!
考虑这样一个字符串 AAABB
如果我们只取一个B 即AAAB AAB AB这样的子串 那么B肯定是不满足条件的(子串中不包含一个回文子串 其中包含B)

同理 对于AABBBB 中的 ABBBB ABBB ABB AB 都是不满足条件的

所以我们只需要找出这些子串 把他们减去 就得到的满足条件的子串数目!!!

具体如何求呢?
从头到尾 以及 从尾到头 进行两次遍历
每当字符发生改变时 记录下上一个连续相同字符的字符串长度 用总子串数目减去这个长度即可(从尾到头时也是如此)
TIPS:由于对于一个串 AAABBB 我们在两次遍历时 会将子串AB统计两次 所以会重复计数 故我们 可以在任一次遍历时加上1即可


总结

比较烧脑的思考题TAT


代码

60分的暴力
#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
#define MAXN 300010
using namespace std;
int n,ans=0;
bool a[MAXN];
string s;
//暴力做法是枚举每个子串(长度大于等于2的) 然后判断每个子串是不是回文串
void check(int st,int ed)
{
    for(int i=st;i<=ed;i++) a[i]=0;
    for(int i=st;i<ed;i++)
    {
        for(int j=i+1;j<=ed;j++)
        {
            int x=i,y=j;
            while(x<y)
            {
                if(s[x]!=s[y])  break;
                x++;y--; 
            }
            if(x>=y)    for(int k=i;k<=j;k++)   a[k]=true;
        }
    }
    int i;
    for(i=st;i<=ed;i++)
        if(a[i]==false) break;
    if(i==ed+1) ans++;
}
int main()
{
    cin>>n;
    cin>>s;
    for(int i=0;i<n-1;i++)//计算所有属于一个
    {
        for(int j=i+1;j<n;j++)
        //从s[i]到s[j]是枚举的所有len>=2的子串
        {
            check(i,j);
        }
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}
满分代码
#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
using namespace std;
string s;
long long  tnt,n;
int main()
{
    cin>>n;
    cin>>s;
    tnt=(n-1)*n/2;//n-1 + n-2 + n-3 + ···· + 1
    long long num=1;
    for(int i=1;i<n;i++)
    {
        if(s[i]!=s[i-1])
        {
            tnt=tnt-num;// 对于AAABBB这样的 AAAB AAB AB都不满足delicious的定义 因为B不属于某一个回文子串
            tnt++;//等会还要从后向前遍历 遍历时会重复搜索到AB这个串 故加一
            num=1;
            //故减去串的长度
        }
        else num++;//num记录当前A串/B串的长度
    }
    num=1;
    for(int i=n-2;i>=0;i--)
    {
        if(s[i]!=s[i+1])
        {
            tnt=tnt-num;//对于AAABBB这样的串 还需要减去 ABBB ABB AB 
            num=1;
        }else num++;
    }
    cout<<tnt<<endl;
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值