本次填空题偏简单,编程大题有难(keng)度(die),4个钟,1个钟ak填空,3个钟写编程大题时间还是很紧张,算法以暴力为主,并没有时间想最优解。
#include
using namespace std;
/*
答案:563
*/
bool is(int year) {
while (year) {
if (year % 10 == 2) return true;
year /= 10;
}
return false;
}
int main() {
int res = 0;
for (int i = 1; i <= 2020; i++) if (is(i)) res++;
cout << res << endl;
return 0;
}
读完题目,易知这是道bfs的模板题,从4个点开始,不断向四个方向扩散,唯一比较难办的是如何解决坐标为负数数组会越界的问题,比赛时我想的最直接的方法是将给定一个偏移量base,将坐标上的点进行整体的移动,但是另一种思路更加的简单,用哈希表的思想,将每个坐标映射成一个数且这个数是唯一的,然后可以用哈希表来维护出现过的坐标 ,虽然程序跑起来会比较长
#include
using namespace std;
#define fi first
#define se second
typedef long long LL;
typedef pair<int, int> PII;
int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};
/*
答案:20312088
*/
// 映射函数f,系数可以随便取,原则是让每个坐标唯一对应一个数
LL f(LL u, LL v) {
return u * 2333333 + v;
}
LL bfs() {
queue<PII> q;
// unordered_set是c++11的东西,可以换成set,不过unordered_set的查找效率会比set高
unordered_set<LL> se;
// 先把四个点插入进来
q.push({0, 0});
q.push({2020, 11});
q.push({11, 14});
q.push({2000, 2000});
se.insert(f(0, 0));
se.insert(f(2020, 11));
se.insert(f(11, 14));
se.insert(f(2000, 2000));
int cnt = 0;
LL res = 4;
while (!q.empty()) {
int sz = q.size();
// bfs第i层对应经过了i分钟
for (int i = 1; i <= sz; i++) {
auto t = q.front();
q.pop();
for (int j = 0; j < 4; j++) {
int u = dx[j] + t.fi, v = dy[j] + t.se;
// 判重
if (se.count(f(u, v))) continue;
se.insert(f(u, v));
q.push({u, v});
res++;
}
}
if (++cnt >= 2020) return res;
}
}
int main() {
cout << bfs() << endl;
return 0;
}
数论题,首先由算术基本定理我们可以给出一个数被分解后质数及其个数
N = P 1 a 1 P 2 a 2 P 3 a 3 . . . P n a n N = P_{1}^{a_1}P_{2}^{a_2}P_{3}^{a_3}...P_{n}^{a_n} N=P1a1P2a2P3a3...Pnan
其中 P i P_i Pi为质数, a i a_i ai为正整数
我们可以推出一条性质,N的约数的个数为 ( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) . . . ( a n + 1 ) ({a_1} + 1)({a_2} + 1)({a_3} + 1)...({a_n} + 1) (a1+1)(a2+1)(a3+1)...(an+1) 这里可以用组合数学 (背包思想) 的办法来解释,约数一定是由不同的质因数组成的,那么每种质数我选择若干个,假设质数 P i P_i Pi可以选0,1,2, a i a_i ai个,一共有( a i a_i ai + 1)种方法,根据乘法原理,把每种质数选择的个数相乘起来即可
#include
using namespace std;
typedef long long LL;
LL cnt[110];
/*
答案:39001250856960000
*/
// 试除法求质因数
void get(int x) {
for (int i = 2; i <= x / i; i++) {
if (x % i == 0) {
while (x > 1 && x % i == 0) cnt[i]++, x /= i;
}
}
if (x > 1) cnt[x]++;
}
int main() {
for (int i = 1; i <= 100; i++) get(i);
LL res = 1;
for (int i = 2; i <= 100; i++) res *= (cnt[i] + 1);
cout << res << endl;
return 0;
}
经典dp问题,这题和递增子序列求方案数相比,多了一个条件,相同的递增子序列只算1次,我们可以先从递增子序列求方案数(包含重复的)开始思考, f [ i ] f[i] f[i]表示以 a [ i ] a[i] a[i]结尾的字符的递增上升子序列方案数,很容易写出状态转移方程
f [ i ] = f [ i ] + f [ j ] , a [ i ] > a [ j ] , j < i < = n f[i] = f[i] + f[j], a[i] > a[j],j < i <= n f[i]=f[i]+f[j],a[i]>a[j],j<i<=n
本质不同递增子序列说白了就是同一个递增子序列只算一次,那么上式最后得出的结果一定是有重复计数的,所以我们要把这一部分剪掉,我们可以重新定义一下 f [ i ] f[i] f[i]表示以i结尾的不同递增子序列的个数,当 i < j , a [ i ] = = a [ j ] i < j, a[i] == a[j] i<j,a[i]==a[j]显然以a[i]结尾的递增子序列方案数都包含在了f[i]当中了,那么f[j]就不必再去计算i前面的递增子序列方案数了,所以碰到 i < j , a [ i ] = = a [ j ] i < j, a[i] == a[j] i<j,a[i]==a[j],break就ok了
#include
/*
答案:3616159
输入:
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
*/
using namespace std;
typedef long long LL;
const int N = 1010;
string str;
// 记录字符i是否出现过
int st[N], f[N];
int main() {
cin >> str;
int n = str.size();
for (int i = 0; i < n; i++) {
if (!st[str[i] - 'a']) f[i] = 1;
st[str[i] - 'a'] = true;
/* 细节:这里需要逆序统计,因为正序统计f[i]加入的方案数时,
万一碰到a[i] == a[j]
显然f[i]正序统计的方案数会和f[j]有重复的方案
*/
for (int j = i - 1; j >= 0; j--) {
if (str[i] > str[j]) f[i] += f[j];
else if (str[i] == str[j]) break;
}
}
// 最后别忘记统计所以以str[i]结尾的所有方案数
LL res = 0;
for (int i = 0; i < n; i++) res += f[i];
cout << res << endl;
return 0;
}
这个问题很抽象但是考场一下想到用dfs其实这题不用dfs也能做,将1-16放到格子里,时间复杂度度为 O ( 16 ! ) O(16!) O(16!),全排列问题然后用判断一下该方案是否合法, 分析问题可以得到的合法的方案即:从1出发往4个方向走,走16次,每个格子有且仅被走过一次。这里还是用dfs解吧
#include
using namespace std;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int st[5][5], res = 0;
/*
答案:552
*/
void dfs(int cnt, int u, int v) {
if (cnt >= 16) {
res++;
return;
}
for (int i = 0; i < 4; i++) {
int x = dx[i] + u, y = dy[i] + v;
if (x <= 0 || y <= 0 || x > 4 || y > 4 || st[x][y]) continue;
st[x][y] = 1;
dfs(cnt + 1, x, y);
// 回溯
st[x][y] = 0;
}
}
int main() {
// 枚举起点
for (int i = 1; i <= 4; i++)
for (int j = 1; j <= 4; j++) {
st[i][j] = 1;
dfs(1, i, j);
st[i][j] = 0;
}
cout << res << endl;
return 0;
}
本次填空题注重基础算法的应用,如简单模拟、bfs、简单数论、dfs、dp考点比较全面,读题细心一点就不会有太大问题