markdown手写笔记 && 2024年3月19日广州大学ACM校赛预选赛复盘
经验一:优化尽可能写。
因为可能代码中就是因为某种特殊情况而导致算法超时,你要是考虑到了这种特殊情况,对代码进行优化,就能提高算法效率。
例如:在兔子分萝卜题目中:
题目描述:https://vjudge.net/contest/616248#problem/E
优化前的代码:
#include<iostream>
using namespace std;
int main()
{
int n, m, k;
cin >> n >> m >> k;
int a = 1; // a波及左右两边的兔子个数,给喜爱兔子的萝卜数量
int c = 1; // 每次给多一个萝卜给最喜爱兔子时,需要消耗给多个兔子的萝卜数量
m -= n; // 预分配每个兔子一个萝卜,剩余萝卜数量m
while (m > 0)
{
if (k + a <= n) // 判断当前波及范围右边是否有兔子
c++;
if (k - a > 0) // 判断当前波及范围左边是否有兔子
c++;
m -= c; // 波及一次消耗萝卜
a++; // 给喜爱的兔子萝卜数量+1,波及范围扩大 // 若是剩余萝卜数量少于波及范围的话,肯定剩余萝卜会分到给最喜爱的萝卜
}
cout << a;
return 0;
}
添加优化代码
if (n == 1) // 优化,一个兔子时,所有萝卜给它就完了
{
cout << m;
return 0;
}
这题就是测试点6没有通过
Test6:
Input
1 1000000000 1
Output
1000000000
经验2 :掌握不了数学逻辑规律再去暴力求解。
这是我的老毛病了,直接拿下面这道例题来辅助理解这句话的含义。
数数题
题目连接:https://vjudge.net/contest/616248#problem/B
这道题,我看到之后,想都没想,直接使用暴力,遍历每一个数,从每个数的首位开始遍历,若是该数出现两个非0的数,则判断下一个数;否则该数为Round数。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
string str = to_string(i);
int count = 0;
for (int i = 0; i < str.size(); i++)
{
if (count == 2)
break;
if (str[i] != '0')
count++;
}
if (count < 2)
cnt++;
}
cout << cnt << endl;
}
return 0;
}
代码思路很简单,编写也非常顺利,但是在提交后,我发现了时间超时。
回过头看时,我这里的时间复杂度为O(n^2),确实很高。
于是乎,我开始寻找有没有更简单的方法。我试着随便找个数,在草稿纸上写出1到该数的所有Round数。我发现了规律:Round数无外乎就是首位不为0,其余位均为0的数。那么,假设给定的是个4位数首位为k,那么1~9+10、20…90、100、00…k00为所有的Round数。根本无需遍历,只要直接计算就可以得到结果。
代码比前面的暴力算法还要好写。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int cnt = 0;
string str = to_string(n);
int num = str.size();
cnt += (num - 1) * 9;
int firstW = str[0] - '0';
cnt += firstW;
cout << cnt << endl;
}
return 0;
}
经验3:与自己学过的知识点相比较。
话不多说,直接上“原点子串”题目连接:https://vjudge.net/contest/616248#problem/D
我们来分析一下这道题。
1、回到原点,怎么定义回到原点呢?坐标为(0,0)时。那么想当然的,就要定义上下左右移动是什么的。这里定义为URDL分别为(1,0)、(0,1)、(0,-1)、(-1,0)。
2、题目要求求出所有子串。这里科普一下:子串的定义是:连续的一小段。与之相似的名词是子序列。子序列的定义是:不连续的一小段。例如:有字符串abcdefg。那么有子串abc、子串bcd;有子序列abc、abdg。由此可知,子串是子序列,子序列不是子串。
3、我们只需要将子序列每个字符对应的“坐标变化”进行“相加”,判断结果是否为(0,0),即判断是否回到了原点。
4、如果你学过了前缀和,当你理顺了上述3点,你应该就知道用这个方法啦。在输入时就将前面的所有“坐标变化”之和存储起来。后面求子串时,就只需要用某个前缀和来减去另外一个前缀和,非常的方便。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
int n;
cin >> n;
vector<char> ax(n+1);
vector<int> sx(n+1);
vector<int> sy(n+1);
for (int i = 1; i <= n; i++)
{
cin >> ax[i];
int tempX = 0;
int tempY = 0;
if (ax[i] == 'U')
{
tempX = 1;
tempY = 0;
}
else if (ax[i] == 'R')
{
tempX = 0;
tempY = 1;
}
else if (ax[i] == 'L')
{
tempX = 0;
tempY = -1;
}
else
{
tempX = -1;
tempY = 0;
}
sx[i] = sx[i - 1] + tempX;
sy[i] = sy[i - 1] + tempY;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
{
for (int j = i - 1; j >= 0; j--)
{
if (sx[i] - sx[j] == 0 && sy[i] - sy[j] == 0)
cnt++;
}
}
cout << cnt;
return 0;
}
顺带再记录一下写出来的另外两道题目。
A-错误的减法https://vjudge.net/contest/616248#problem/A
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
int k;
cin >> n>>k;
while (k--)
{
if (n % 10 != 0)
n--;
else
{
n /= 10;
}
}
cout << n;
return 0;
}
这道题很简单,没什么好说的。
C-字符串划分https://vjudge.net/contest/616248#problem/C
这道题一开始还不知道怎么写呢。后面还是沉下心来,拿草稿纸演算并思考了一下:
1、我们可以组成什么字符串。我们拥有aa、bb、aaa、bbb。那么,我们便可以组成2~无穷个连续的a字符串和b字符串。
2、我们组成不了什么字符串。单个a和单个b。
3、有了上面两个发现,我们便得出了判断能否组成字符串的方法:统计某个字符连续的次数,若次数为1,则输出NO;否则遍历它后面的字符,接着重复判断重复出现的次数,知道输出NO或者是字符串的每个字符遍历完了。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
string str;
cin >> str;
int x = 1;
bool flag = 0;
for (int i = 1; i < str.size(); i++)
{
if (str[i] == str[i - 1])
{
x++;
continue;
}
else
{
if (x <= 1)
{
cout << "NO" << endl;
flag = 1;
break;
}
else
{
x = 1;
}
}
}
if (x > 1)
cout << "YES" << endl;
else if(!flag) // 单个字符时,在这里进行输出!!!
cout << "NO" << endl;
}
return 0;
}
这里又有话说了,一开始我没有考虑单个字符的情况,要不是测试样例中有单个字符,我就要提交上去送分了!!!所以,我又得出了如下代码。
经验4:自己写多几个测试样例,重复考虑特殊情况,以检验代码是否完整。
写在最后,很高兴您能看到这里。
如果您是大三,和我一样还没有练过几道算法题(我仅练习了半个月),那么我强烈建议如果不考研的同学的可以每天写一写算法题,已写带学。
如果您是大一大二甚至是高中的学弟,我更加强烈建议你们现在开始每天学算法、练学法,不要想着自己的基础知识还没学完,自己的《数据结构》没学或者是《算法设计》没学。过来人告诉你,就算等你到时候学了,你没有练习,你也会和我一样,啥都不会。什么动态规划、贪心算法,你就会知道你上课听过,但是你没有动手写过,你不能深刻理解其中的微妙。
来吧!和我一起练起来,你以后考研或者是就业,肯定都有些许帮助。