随便写点, 就当今天没有白过~。
题目:
在计算机存储当中, 15.125GB是多少MB?
分析:
说的是在计算机存储当中, 细品一下, 第一题应该不会诈我, 嗯, 拿起计算器敲一遍: 15.125 X 1024 = 15488
。不过既然题目强调在计算机存储当中来考虑这个数值, 会不会有其他情况下进制不同? 于是去的查了查, 有人说在硬盘存储 和 通信中是1000
进制, 卧槽? 第一题我感觉可能就被坑了。。。。。
题目:
1200000 有多少个约数, 限制正约数?
分析:
刚看到约数还不知道什么鬼东西, 原来就是因数…, 要是题目不说限制正约数, 估计又要炸一题…, 算的应该是96
(总感觉算错了…)
题目:
一颗包含2019个结点的树, 最多包含多少个叶结点?
分析:
再细品一下, 没说二叉树, 那就一个根节点, 2018
个叶子结点吧。
题目:
1~2019有多少个数包含9?
分析:
要是想口算绝对是憨憨…, 老老实实写个程序吧, 应该是 544
题目:
问题描述
小明对类似于 hello 这种单词非常感兴趣,这种单词可以正好分为四段,第一段由一个或多个辅音字母组成,第二段由一个或多个元音字母组成,第三段由一个或多个辅音字母组成,第四段由一个或多个元音字母组成。
给定一个单词,请判断这个单词是否也是这种单词,如果是请输出yes,否则请输出no。
元音字母包括 a, e, i, o, u,共五个,其他均为辅音字母。
输入格式
输入一行,包含一个单词,单词中只包含小写英文字母。
输出格式
输出答案,或者为yes,或者为no。
样例输入
lanqiao
样例输出
yes
样例输入
world
样例输出
no
评测用例规模与约定
对于所有评测用例,单词中的字母个数不超过100。
C++
分析:
嗯, 上过编译原理的课, 自动机还是知道的, 老老实实写一个自动机。
代码:
/**
* 典型的 自动机 题目
**/
#include
using namespace std;
bool flag = false; // 标记找辅音还是找元音
bool isyuan(char c)
{
if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') return true;
else return false;
}
bool is(string s)
{
int i = 0, pre_i = 0;
while(i < s.size() && !isyuan(s[i])) i++; // 识别第一个辅音
if(i == pre_i) return false;
pre_i = i;
while(i < s.size() && isyuan(s[i])) i++; // 识别第二个元音
if(i == pre_i) return false;
pre_i = i;
while(i < s.size() && !isyuan(s[i])) i++; // 识别第三个辅音
if(i == pre_i) return false;
pre_i = i;
while(i < s.size() && isyuan(s[i])) i++; // 识别第四个元音
if(i == pre_i) return false;
return i == s.size();
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
string s;
cin >> s;
if(is(s)) cout << "yes" << endl;
else cout << "no" << endl;
return 0;
}
题目:
问题描述
在数列 a[1], a[2], …, a[n] 中,如果对于下标 i, j, k 满足 0 给定一个数列,请问数列中有多少个元素可能是递增三元组的中心。
输入格式
输入的第一行包含一个整数 n。
第二行包含 n 个整数 a[1], a[2], …, a[n],相邻的整数间用空格分隔,表示给定的数列。
输出格式
输出一行包含一个整数,表示答案。
样例输入
5
1 2 5 3 5
样例输出
2
样例说明
a[2] 和 a[4] 可能是三元组的中心。
评测用例规模与约定
对于 50% 的评测用例,2 <= n <= 100,0 <= 数列中的数 <= 1000。
对于所有评测用例,2 <= n <= 1000,0 <= 数列中的数 <= 10000。
C++
分析:
要是想套三个循环头给你打爆…, 维护两个数组small
和 big
, small[i]
表示 0~i
下标的当中的最小数, big[i]
表示i~n
当中的最大数, 则O(n)
扫一遍, 对于某个数a[i]
, 如果 small[i-1] < a[i] < big[i + 1]
, 这就是一个中心。
代码:
#include
using namespace std;
int a[1000 + 5];
int big[1000 + 5]; // i ~ n 范围内的最大数
int small[1000 + 5]; // 0~i 范围内的最小数
int main()
{
#ifdef LOCAL
freopen("F.in", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n; cin >> n;
for(int i = 0; i < n; ++i) cin >> a[i];
small[0] = a[0], big[n - 1] = a[n - 1];
for(int i = 1; i < n; ++i) small[i] = min(small[i - 1], a[i]);
for(int i = n - 2; i >= 0; --i) big[i] = max(big[i + 1], a[i]);
int ans = 0;
for(int i = 1; i < n - 1; ++i)
{
if(a[i] > small[i - 1] && a[i] < big[i + 1]) ans += 1;
}
cout << ans << endl;
return 0;
}
题目:
问题描述
一个正整数如果任何一个数位不大于右边相邻的数位,则称为一个数位递增的数,例如1135是一个数位递增的数,而1024不是一个数位递增的数。
给定正整数 n,请问在整数 1 至 n 中有多少个数位递增的数?
输入格式
输入的第一行包含一个整数 n。
输出格式
输出一行包含一个整数,表示答案。
样例输入
30
样例输出
26
评测用例规模与约定
对于 40% 的评测用例,1 <= n <= 1000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000。
分析:
没啥好说, O(n)
扫一遍判断就行。
#include
using namespace std;
bool is(int n)
{
int pre = -1;
while(n)
{
if(pre == -1) pre = n % 10;
else
{
int t = n % 10;
if(pre < t) return false;
pre = t;
}
n /= 10;
}
return true;
}
int main()
{
#ifdef LOCAL
freopen("G.in", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n, ans = 0; cin >> n;
for(int i = 1; i <= n; ++i)
{
if(is(i)) ans += 1;
}
cout << ans << endl;
return 0;
}
题目:
问题描述
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1。
小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。
这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。
请告诉小明,k 个月后空地上哪些地方有草。
输入格式
输入的第一行包含两个整数 n, m。
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。
输出格式
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
样例输入
4 5
.g…
…
…g…
…
2
样例输出
gggg.
gggg.
ggggg
.ggg.
评测用例规模与约定
对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= k <= 1000。
分析:
典型的BFS
题目, 节点增广就完事了.
代码:
/**
* BFS 边界扩展
*/
#include
using namespace std;
struct Node {
int r, c;
Node(int _r, int _c) : r(_r), c(_c){}
}; // BFS结点
int G[1000 + 1][1000 + 1]; // 草地
int dr[4] = {-1, 0, 1, 0}; // 上右下左
int dc[4] = {0, 1, 0, -1};
int main()
{
#ifdef LOCAL
freopen("H.in", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
queue Q;
int n, m, k; cin >> n >> m;
// 输入
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
char c; cin >> c;
if(c == 'g')
G[i][j] = 1, Q.push(Node(i, j));
else
G[i][j] = 0;
}
}
cin >> k;
// BFS
int month = 0;
while(!Q.empty() && month < k)
{
int size =Q.size();
while(size--)
{
Node node = Q.front(); Q.pop();
for(int i = 0; i < 4; ++i)
{
int nr = node.r + dr[i], nc = node.c + dc[i];
// 没有越界, 且没有被访问过
if(nr < 0 || nr >= n || nc < 0 || nc >= m || G[nr][nc] == 1) continue;
// 此位置长草
G[nr][nc] = 1;
// 将此位置添加到队列等待进一步处理
Q.push(Node(nr, nc));
}
}
month += 1;
}
// 输出最终结果
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
{
if(G[i][j] == 1) cout << 'g';
else cout << ".";
}
cout << endl;
}
return 0;
}
题目:
问题描述
小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
请计算,对于给定的 n,有多少种满足条件的序列。
输入格式
输入一行包含一个整数 n。
输出格式
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
样例输入
4
样例输出
7
样例说明
以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4
评测用例规模与约定
对于 20% 的评测用例,1 <= n <= 5;
对于 50% 的评测用例,1 <= n <= 10;
对于 80% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000。
分析:
方案一:
题目也太经典了, 完全可以映射出一大类题目: 画出解答树->寻找子问题->记忆化搜索->超时的话要么继续优化搜索, 要么转成动态规划的递推方式。我只想到对80%
数据OK
的 记忆化搜索, 还有20%超时, 转动态规划有大问题, 等等官网看看有没有题解出来瞅瞅…
方案二: 优化方案一
原状态转移方程为:
f ( i , k ) = ∑ j = 1 k − 1 f ( j , ∣ i − j ∣ ) f(i, k) = \sum_{j = 1}^{k-1}f(j, |i-j|) f(i,k)=j=1∑k−1f(j,∣i−j∣)
其中 i i i表示解答树当中某个结点的值, k k k 表示 i i i 与父亲结点的绝对值差。可见这个解答树非分支数量不确定, 导致在 D F S DFS DFS某个结点的时候会带一个循环, 这可能是导致时间效率低下的罪魁祸首, 方案二重新改写状态方程, 能将这个循环干掉, 通过将解答树的分支数量减少到少于 2 个 2个 2个, 具体如下:
f ( i , j ) = f ( i , j − 1 ) + f ( j , ∣ i − j ∣ − 1 ) f(i, j) = f(i, j - 1) + f(j, |i - j| - 1) f(i,j)=f(i,j−1)+f(j,∣i−j∣−1)
其中 i i i表示某个解答树当中某个结点的值, 而 j j j表示其孩子的最大值是多少, 因此 f ( i , j ) f(i, j) f(i,j) 表示 结点 i i i 在其孩子的值 ≤ j \leq j ≤j 时候的序列数, 以此就干掉了那个循环, 虽然导致了解答树深度变大, 但是经过测试, 效率确实显著提升。
方案一代码:
/**
* 统计解答树的除根节点以外的结点的数量
*/
#include
using namespace std;
const int MODE = 10000;
int dp[1000 + 1][1000]; // 存放重复解
// 前一项和当前项
int dfs(int pre, int n)
{
int _abs = abs(pre - n);
// 重复问题
if(dp[n][_abs])
{
return dp[n][_abs];
}
// dfs其子项, 后一项小于前两项的绝对值差
int ans = 0;
for(int i = 1; i < _abs; ++i)
{
ans = (ans + dfs(n, i)) % MODE;
}
// 小心不要忘记当前结点的数量;
return dp[n][_abs] = (ans + 1) % MODE;
}
int main()
{
#ifdef LOCAL
freopen("", "r", stdin);
#endif
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int ans = 0, n; cin >> n;
for(int i = 1; i <= n; ++i)
{
ans = (dfs(n, i) + ans) % MODE;
//cout << ans << endl;
}
cout << ans << endl;
return 0;
}
方案二代码:
/**
转变状态转移方程为 dp(i, j) 其表示为当前节点为i, 后继节点从 1 - j 的总的方案数。
dp(i, j) = dp(i, j - 1) + dp(j, |i - j | - 1);
**/
#include
using namespace std;
const int MAXN = 1e3 + 1;
const int MODE = 1e4;
int dp[MAXN][MAXN];
int dfs(int i, int j)
{
if(j <= 0) return 0;
if(dp[i][j]) return dp[i][j];
else return dp[i][j] = (dfs(i, j - 1) + dfs(j, abs(i - j) - 1) + 1) % MODE;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n; cin >> n;
cout << dfs(n, n) << endl;
return 0;
}
题目:
问题描述
小明要组织一台晚会,总共准备了 n 个节目。然后晚会的时间有限,他只能最终选择其中的 m 个节目。
这 n 个节目是按照小明设想的顺序给定的,顺序不能改变。
小明发现,观众对于晚上的喜欢程度与前几个节目的好看程度有非常大的关系,他希望选出的第一个节目尽可能好看,在此前提下希望第二个节目尽可能好看,依次类推。
小明给每个节目定义了一个好看值,请你帮助小明选择出 m 个节目,满足他的要求。
输入格式
输入的第一行包含两个整数 n, m ,表示节目的数量和要选择的数量。
第二行包含 n 个整数,依次为每个节目的好看值。
输出格式
输出一行包含 m 个整数,为选出的节目的好看值。
样例输入
5 3
3 1 2 5 4
样例输出
3 5 4
样例说明
选择了第1, 4, 5个节目。
评测用例规模与约定
对于 30% 的评测用例,1 <= n <= 20;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 100000,0 <= 节目的好看值 <= 100000。
分析:
离散化的小技巧, 想想当年看俄罗斯方块儿还是没有白看 , 两次排序, 第一次按照喜欢度排序, 得到m个数, m个数在按照下标排序就OK
。
这个第一个节目尽可能好看, 第二个节目在第一个节目的基础上尽可能好看, 说明第一个节目选择的范围为 a[0]~a[n -m]
, 第二个节目选择的范围在 a[last + 1] ~ a[n -m + 1]
, 依次类推下去, 直到选择出来所有的节目(这题意实在是太难理解了), 其中涉及到多次区间范围的内最值查询, 因此可以使用线段树来解决最值查询问题, 代码如下:
代码:
#include
#include
#include
#include
using namespace std;
const int MAXN = 1e5;
struct Node
{
int l; /* 当前结点代表的区间的左边界*/
int r; /* 当前节点代表的区间的右边界*/
int m_index; /* 当前区间内的最大值的下标*/
}tree[4 * MAXN]; /* 线段树的节点*/
int a[MAXN + 1];
/**
从根节点开始构建二叉树, 根节点从1开始计算。
**/
void build(int root, int l, int r)
{
tree[root].l = l;
tree[root].r = r;
// 构建叶子结点
if(l == r)
{
tree[root].m_index = l;
// printf("%d [%d, %d] %d\n", root, l, r, tree[root].m_index);
return;
}
// 构建其他结点
int mid = (l + r) >> 1;
int left = root << 1;
int right = left + 1;
build(left, l, mid);
build(right, mid + 1, r);
tree[root].m_index = a[tree[left].m_index] > a[tree[right].m_index] ? tree[left].m_index : tree[right].m_index;
// printf("%d [%d, %d] %d\n", root, l, r, tree[root].m_index);
}
/**
* @param root : 当前节点
* @param ql : 目标区间左边界
* @param qr : 目标区间右边界
* @return : [ql, qr] 区间内最大值
**/
int query(int root, int ql, int qr)
{
// printf("[%d, %d] -- [%d, %d]\n", tree[root].l, tree[root].r, ql, qr);
// Sleep(1000);
// 如果节点区间在目标区间内, 则直接返回节点区间当中的最大值
if(tree[root].l >= ql && tree[root].r <= qr) return tree[root].m_index;
int mid = (tree[root].l + tree[root].r) >> 1;
// 如果节点区间的左孩子和目标区间有交集
int ans = -1;
if(ql <= mid) ans = query(2 * root, ql, qr);
// 如果节点区间的右孩子和目标区间有交集
if(qr > mid)
{
// 目标区间和节点区间的左孩子没有交集
if(ans == -1)
{
ans = query(2 * root + 1, ql, qr);
}
else
{
int _index = query(2 * root + 1, ql, qr);
if(a[_index] > a[ans])
ans = _index;
}
}
return ans;
}
int main()
{
#ifdef LOCAL
freopen("J.in", "r", stdin);
#endif
int n, m; cin >> n >> m;
for(int i = 1; i <= n; ++i) cin >> a[i];
build(1, 1, n);
int _index = query(1, 1, 3);
cout << a[_index] << endl;
int ql = 1, qr = n - m + 1;
bool first = true;
while(m--)
{
int _index = query(1, ql, qr);
cout << (first ? "" : " ") << a[_index], first = false;
ql = _index + 1;
qr++;
}
cout << endl;
}