Array&STL 解题报告

写在前面

  • 这是一篇友好的解题报告

  • 为响应勤俭节约的核心价值观,题意省略背景,使用简洁中文

  • 为了便于理解,每段思路下都会有代码

  • 每题讲解一种个人认为比较简单易懂的方法

  • 每题的标题均链接了洛谷(众所周知,洛谷的题解很友好)或者个人认为比较好的题解

  • 本篇题解仅供参考,有时间的同学可以通过搜索题目了解每题更多的解法学习

  • 由任何问题均可以通过qq,询问我

  • 由于匆忙赶制,所以可能会有错误或用语不规范请谅解

CF144A Arrival of the General

题意

  • 输入数组大小n ( 2 < = n < = 100 ) (2<=n<=100) 2<=n<=100,

  • 依次输入a1,a2,a3......an ( 1 < = a < = 100 ) (1<=a<=100) 1<=a<=100)

  • 求经过几次交换(只可以前后相邻的两个交换) 保证第一个数最大,最后一个数最小(中间的大小没影响)


思路

首先我们可以通过遍历找到最左边的最大值ma_val和最右的最小值mi_val

即为ma_posmi_pos

int mi_pos, ma_pos, mi_val, ma_val;
mi_val = a[1], mi_pos = 1;
for (int i = 2; i <= n; i++)
    if (a[i] <= mi_val)		//由于需要找到最右边的最小值,所以可以等于
        mi_val = a[i], mi_pos = i;
ma_val = a[1], ma_pos = 1;
for (int i = 2; i <= n; i++)
    if (a[i] > ma_val)
        ma_val = a[i], ma_pos = i;

由于只能相邻的交换,所以

我们只需要先将最小值交换到n,再将最大值交换到1即为最小次数

由于每次交换后,他的位置变化为1,所以从x交换到y的步数为 y − x y-x yx ( y > x ) (y>x) (y>x)

要注意mi_pos<ma_pos,两者交换的过程会有交汇,ma_pos会被向前交换一位

int ans = 0;
ans += (n - mi_pos);	//最小值交换到n
if(mi_pos<ma_pos)
    ma_pos--;
ans += ma_pos - 1;	//最大值交换到1
cout << ans << '\n';

复杂度 O ( n ) O(n) O(n)


CF469A I Wanna Be the Guy

题意

X和小Y最近在玩一个叫做I wanna be the guy的游戏,这个游戏有n个关卡。把n个关卡都通过后,那么就完成游戏了。

可是小X只能过p关,小Y只能过q关。因为他们能力有限,所以他们决定合作。

给出小X和小Y能通过的关卡,他们能完成这个游戏吗?如果可以,输出I become the guy.,否则,输出Oh, my keyboard!


思路

读入p个数字和q个数字,判断1n是否都出现过即可

我们可以开一个标记数组vis[x],表示x这个数是否出现过

我们遍历 p + q p+q p+q个数字,对于每个数字x v i s [ x ] + + vis[x]++ vis[x]++表示x这个数字出现过一次

for (int i = 1; i <= p; i++)	//遍历p个数
    vis[a[i]]++;
for (int i = 1; i <= q; i++)	//遍历q个数
    vis[b[i]]++;

最后,我们查找vis[1]vis[n]是否有一个为0即可

for (int i = 1; i <= n; i++)
{
    if(vis[i]==0)		//只要找到一个,直接退出一个
    {
        printf("Oh, my keyboard!");
        return 0;
    }
}
printf("I become the guy.\n");	//没有一个为0,即可通关
return 0;

复杂度 O ( n ) O(n) O(n)


杨辉三角

题意

多组测试,输出杨辉三角形

思路

杨辉三角形规律(即为组合数规律)

每行第一个和最后一个为1 c o m b [ i ] [ 0 ] = c o m b [ i ] [ i ] = 1 comb[i][0]=comb[i][i]=1 comb[i][0]=comb[i][i]=1

中间的每个数等于它上方两数之和( c o m b [ i ] [ j ] = c o m b [ i − 1 ] [ j − 1 ] + c o m b [ i − 1 ] [ j ] comb[i][j]=comb[i-1][j-1]+comb[i-1][j] comb[i][j]=comb[i1][j1]+comb[i1][j]

需要注意的是杨辉三角形包含第0行,为11

img

我们可以先计算出前30行的组合数,当询问时输出即可(预处理)

for (int i = 0; i < 30; i++)
{
    comb[i][0] = comb[i][i] = 1;
    for (int j = 1; j < i; j++)
        comb[i][j] = comb[i - 1][j - 1] + comb[i - 1][j];
}

多组输入,每行最后一个数字后面没有空格

注意输出格式,每个杨辉三角形后面都有一个空行

while (cin >> n)//多组输入
{
    for (int i = 0; i < n; i++)//从第0行开始输出
    {
        for (int j = 0; j < i; j++)
            cout << comb[i][j] << ' ';
        cout << comb[i][i] << '\n';	//最后一个数直接输出换行
    }
    cout << '\n';//每个三角形均输出空行
}
return 0;

复杂度 O ( n 2 ) O(n^2) O(n2)

CF208A Dubstep

题意

Vasya有一句歌词(可能含有空格)

他在第一个单词之前和最后一个单词之后加上若干(可以为0)个"WUB"

再每个单词之间加上若干(至少为1)个"WUB",转变成一个新的字符串(无空格)

"I AM X" 可以变成 "WUBWUBIWUBAMWUBWUBX" 而不能变成 "WUBWUBIAMWUBX" 现在给出转变后的字符串,求出原来的字符串


思路

本题为字符串处理题,不懂字符数组或String同学可以预习下这一部分

这里讲字符数组的一种解法


字符数组简介(由于简介所以部分用语并不规范,请谅解)

char s[1000]:定义字符数组

cin>>s:可以读入字符数组

strlen(s):可以得到字符数组的长度

cout<<s:可以输出字符数组

s[i]:可以直接访问第i个字符(第一个下标为0,与数组相同)

注意的是strlen,cout都需要字符数组的最后一位为\0


我们可以遍历字符数组,

  • 遇到WUB,将WUB跳过

  • 将非WUB的字符写入一个新的字符串Str

    遇到下一个WUB时,说明新字符串读入结束,将他输出即可

cin >> s;		//读入字符串
int k = 0;
int len = strlen(s);	//得到字符串的长度
for (int i = 0; i < len;)
{
    if(s[i]=='W'&&s[i+1]=='U'&&s[i+2]=='B')//遇到WUB
    {
        i += 3;//跳过这三个字符
        if(k>0)//k>0表示已经记录新的字符串
        {
            str[k] = 0;//保持字符数组的最后一位为0
            cout << str << ' ';
            k = 0;//将k归0
        }
    }
    else
        str[k++] = s[i], i++;	//将新的字符加入str字符串,并跳到下一位
}
if(k>0)	//最后一个可能也是字符串,没遇到WUB,没输出
{
    str[k] = 0;
    cout << str << ' ';
}

复杂度 O ( n ) O(n) O(n)


CF61A Ultra-Fast Mathematician

题意

  • 给你两个字符串,每个字符串都代表一个二进制下的数

  • 求这两个数异或起来后的值的二进制是多少。

  • 保证给的两个字符串一样长,也就是说可能有字符串会有前导0,同时请保证输出的这个二进制数一样长,也就是说不要去掉前导0

  • 字符串长度小于100


思路

解释一下异或的意思

异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为: 0 ⊕ 0 = 0 , 1 ⊕ 0 = 1 , 0 ⊕ 1 = 1 , 1 ⊕ 1 = 0 0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0 00=010=101=111=0相同为0,不同为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法

所以,我们只要将两个字符串输入,比较每一位,生成一个新的字符串输出即可

a [ i ] = = b [ i ] a[i]==b[i] a[i]==b[i],则 c [ i ] = 0 c[i]=0 c[i]=0,若 a [ i ] ! = b [ i ] a[i]!=b[i] a[i]!=b[i],则 c [ i ] = 1 c[i]=1 c[i]=1

字符数组简介见上题

cin >> a >> b;	//读入两个字符串
int len = strlen(a);	//得到字符串长度
for (int i = 0; i < len; i++)	//遍历两个字符串
    if (a[i] == b[i])
        c[i] = '0';		//相同新字符串该位为'0'
    else
        c[i] = '1';	//反之改位为1
c[len] = 0;	//最后一位保持0
cout << c;
return 0;

复杂度 O ( n ) O(n) O(n)


F - To and Fro

题意:找出字符串的存储规律,输出就行了:
如图模样存储,按列输出就行了。


思路

我们可以发现奇数行是正序的,偶数行是倒序的

我们可以n个,n个遍历数组,将字符加入二维字符数组,还原二维数组

cin >> s;
int len = strlen(s);	//得到长度
int m = len / n;		//m为二维数组行数
int k = 0;	//k为查询的字符数组的第几位
for (int i = 1; i <= m; i++)//矩阵的第i行
{
    if (i % 2 == 1)//奇数行
    {
        for (int j = 1; j <= n; j++)//正序填入
            a[i][j] = s[k++];
    }
    else
    {
        for (int j = n; j >= 1; j--)//倒序填入
            a[i][j] = s[k++];
    }
}

按题意,竖着输出字符即可

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        cout << a[j][i];
cout << '\n';

复杂度$O(n) $


G - 单词数

题意

多组输入

每组一行,每行有多个单词,统计每行不同单词的个数

#结束


思路

此题对于刚接触acm的同学较难

这里介绍一种比较简单的方法

我们可以通过getchar()连续读入字符

  • 当读入小写字母时,将他加入新的字符串str(string 可以直接通过加法连接字符)

    • (不懂string可以点击标题链接,学习字符数组的解法或者学习string)
  • 当读入非小写字母时

    • 说明一个字符串输入完毕,将str加入map(听说yx学姐已经给你们讲过map了)
    • 若读入\n换行符,说明一行已经读入完毕,输出map.size()
map<string, bool> Map;
string str;
char ch;

while ((ch = getchar()) != '#') {
    if (ch >= 'a' && ch <= 'z') {
        str += ch;
    } else {
        if (str.size() > 0) {
            Map[str] = true;
            str = "";
        }
        if (ch == '\n') {
            cout << Map.size() << endl;
            Map.clear();
            str = "";
        }
    }
}

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)


H - 不要62

题意

  • 杭州认为一个数字中包含4或者62是不吉利的
  • 询问从nm包含几个吉利的数字

思路

需要判断一个数字是否位吉利数字,我们需要遍历她的每一位

对于x,我们通过x%10可以得到x的最后一位,然后x/10将最后一位消除

如此我们就得到一个数字的最后一位,重复以上过程值得x==0,我们就能得到一个数字的每一位

int a[7], k = 0;
while (x) {
    a[k++] = x % 10;
    x /= 10;
}

显然,我们的得到的数字是倒序的,所以不要62就变成了不要26

我们遍历即可判断一个数是否吉利

bool check(int x){
    int a[7], k = 0;
    while(x){
        a[k++] = x % 10;
        x /= 10;
    }
    a[k] = 0;
    for(int i=0;i<k;i++)
        if(a[i]==4)
            return false;
        else if(a[i]==2&&a[i+1]==6)
            return false;
    return true;
}

对于每组询问都遍历nm是不现实的,想想都会TLE

所以我们可以暴力预处理好每一位数是否吉利

然后用前缀和优化,即用sum[i]表示0i有多少个吉利的数

那么nm有多少个吉利数即为 s u m [ m ] − s u m [ n − 1 ] sum[m]-sum[n-1] sum[m]sum[n1]

for (int i = 1; i <= 1000000; i++)
    if (check(i))
        sum[i] = sum[i - 1] + 1;
    else
        sum[i] = sum[i - 1];
int n, m;
while (cin >> n >> m) {
    if (n == 0 && m == 0)
        break;
    cout << sum[m] - sum[n - 1] << '\n';
}

复杂度 O ( n ) O(n) O(n)


I - 超级楼梯

题意

有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?


思路

我们可以倒过来想,对于第i台阶,只有可能由i-1阶或i-2阶迈上 ( i > = 3 ) (i>=3) (i>=3)

所以迈上第i阶的方案为,迈上第i-1阶的方案加上迈上第i-2阶的方案

显然第1,2阶方案均为1,细心的同学一定已经发现这就是斐波那契数列了

我们只需要预处理140的方案数,返回询问的值即可

int fib[45] = { 0, 1, 1 };
for (int i = 3; i <= 40; i++)//计算fib数
    fib[i] = fib[i - 1] + fib[i - 2];
int t, n;
cin >> t;
while (t--) {
    cin >> n;
    cout << fib[n] << '\n';
}

复杂度 O ( n ) O(n) O(n)


J - Fibsieve`s Fantabulous Birthday

题意

  • 给出一个矩阵,该矩阵以左下角为中心绕圈填数

  • 询问数字n所在的坐标


思路

img

我们观察到

  • 对角线上的数为 n ∗ ( n − 1 ) + 1 n*(n-1)+1 n(n1)+1
  • 每圈上的数刚好介于两个平方数中间,即只要求出平方根,即可知道层数

上面两个规律是为什么? 可以思考下

  • x=1为起点,对于奇数层上的数递减,偶数层数上的数递增

  • 分类讨论,以对角线上的点为中心点,计算n与对角的差距即可

  • 注意特殊处理平方数和long long

cin >> n;
long long base = sqrt(n * 1.0);	//平方根
if (base * base == n) {	//平方数
    if (base & 1)
        cout << "1 " << base << '\n';
    else
        cout << base << ' ' << 1 << '\n';
    continue;
}
long long limit = base * (base + 1) + 1;//对角线上的数
if (base & 1) {	//奇数层
    if (n < limit)
        cout << base + 1 - (limit - n) << ' ' << base + 1 << '\n';
    else
        cout << base + 1 << ' ' << base + 1 - (n - limit) << '\n';
} else {	//偶数层
    if (n < limit)
        cout << base + 1 << ' ' << base + 1 - (limit - n) << '\n';
    else
        cout << base + 1 - (n - limit) << ' ' << base + 1 << '\n';
}

K - Train Problem I

题意

给出火车站的火车入站顺序和出站顺序

火车站只有一个门,请问是否可能?


思路

模拟栈

因为火车站只有一个门,所以和栈的结构是一样的

后进站的火车必须先出,在里面的火车出不来

所以我们发现这和栈是一样的


栈简介:栈中元素先进后出

st.push(val):将val加入栈

st.top():返回栈顶元素的值

st.pop():弹出栈顶的元素

st.empty():判断栈是否为空


我们知道入栈顺序和出栈顺序

我们可以模拟这个过程

  • 先按序将火车压进栈,并比较当前栈顶与出栈顺序
  • 若当前栈顶元素为下一个出栈的元素,那么他必须马上出栈
    • 因为当下一个元素进栈,第一个出栈的元素只能栈顶元素,产生矛盾
    • 当栈顶元素相同时,要将能出栈的都出栈,而不是只比较一个
  • 若最后栈为空,表示成功,否则失败
  • 过程用数组记录,操作的过程,如1表示入栈,2表示出栈
scanf("%s%s", s1, s2);		//入栈和出栈顺序
int k = 0, j = 0;		//j为下一个出栈元素的下标
for (int i = 0; i < n; i++) {
    st.push(s1[i]);		//入栈
    res[k++] = 1;	//记录入栈
    while (!st.empty() && st.top() == s2[j]) {//若栈顶元素需要出栈
        st.pop();
        res[k++] = 2;	//记录出栈
        j++;
    }
}
if (!st.empty()) {
    cout << "No.\n";
    while (!st.empty())
        st.pop();
} else {
    cout << "Yes.\n";
    for (int i = 0; i < k; i++)
        if (res[i] == 1)
            cout << "in\n";
        else
            cout << "out\n";
}
cout << "FINISH\n";

复杂度: O ( n ) O(n) O(n)


L - ACboy needs your help again!

题意

给出操作方式,FIFO(先进先出)或FILO(先进后出)

请输出out的数

思路

先进先出:队列

先进后出:栈


队列:队列中元素先进先出

q.push(val):将val加入队列

q.front():返回队首元素的值

q.pop():弹出队首的元素


模拟,他的操作即可

FIFO(队列模拟)

queue<int> q;
while (n--) {
    cin >> opt;
    if (opt[0] == 'I') { //in
        cin >> x;
        q.push(x);		//入队
    } else {				//out
        if (!q.empty()) {	//不为空
            cout << q.front() << '\n';
            q.pop();
        } else
            cout << "None\n";
    }
}

FILO(栈模拟)

stack<int> st;
while (n--) {
    cin >> opt;
    if (opt[0] == 'I') {
        cin >> x;
        st.push(x);
    } else {
        if (!st.empty()) {
            cout << st.top() << '\n';
            st.pop();
        } else
            cout << "None\n";
    }
}

复杂度$O(n) $


M - Ugly Numbers

题意

题目要求我们输入一个数字n,输出第n个丑数,丑数的要求是为其质数因子仅有2、3、5。


思路

这里提供一个用set暴力的思路

题目链接了一个更精妙的思路

由于质因子只有2,3,5,所以对于每个数x2*x3*x5*x均为丑数

由于我们要找前1500小的,我们想到一个STL:set,带有去重和排序功能


set简介set可以对加入的数据去重和排序

s.insert(val):插入值为val的元素

s.clear():清空set

s.size()set的大小

set<T>::iterator iter:迭代器iter

set的遍历需要使用迭代器iter

set<int>::iterator iter;
for (iter = s.begin(); iter !s.end(); iter++)
    cout << *iter << ' ';

  • 我们每次只要找到最小的值x,将2*x3*x5*x加入,

  • 再次找到剩余数中的最小值重复以上步骤即可

原理相当于做个完全背包(qwq),当然显然也是对的

set<long long> Set;
Set.insert(1);
set<long long>::iterator iter = Set.begin();
for (int i = 1; i <= 1500; i++, iter++) {
    res[i] = *iter;
    Set.insert(res[i] * 2);
    Set.insert(res[i] * 3);
    Set.insert(res[i] * 5);
}

在提供一种优先队列的写法,需要去重

priority_queue<long long, vector<long long>, greater<long long> > q;
//优先队列,以小的为优先
q.push(1);
for (int i = 1; i <= 1500; i++) {
    while (q.top() == res[i - 1])//去重
        q.pop();
    res[i] = q.top();
    q.pop();
    q.push(res[i] * 2);
    q.push(res[i] * 3);
    q.push(res[i] * 5);
}

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

::iterator iter:迭代器iter`

set的遍历需要使用迭代器iter

set<int>::iterator iter;
for (iter = s.begin(); iter !s.end(); iter++)
    cout << *iter << ' ';

  • 我们每次只要找到最小的值x,将2*x3*x5*x加入,

  • 再次找到剩余数中的最小值重复以上步骤即可

原理相当于做个完全背包(qwq),当然显然也是对的

set<long long> Set;
Set.insert(1);
set<long long>::iterator iter = Set.begin();
for (int i = 1; i <= 1500; i++, iter++) {
    res[i] = *iter;
    Set.insert(res[i] * 2);
    Set.insert(res[i] * 3);
    Set.insert(res[i] * 5);
}

在提供一种优先队列的写法,需要去重

priority_queue<long long, vector<long long>, greater<long long> > q;
//优先队列,以小的为优先
q.push(1);
for (int i = 1; i <= 1500; i++) {
    while (q.top() == res[i - 1])//去重
        q.pop();
    res[i] = q.top();
    q.pop();
    q.push(res[i] * 2);
    q.push(res[i] * 3);
    q.push(res[i] * 5);
}

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值