点此前往练习
小团想要编写一个程序,希望可以统计在M和N之间(M (1) ABCDEF这六个数字均不相同,即A、B、C、D、E和F表示六个不同的数字。 (2) AB+CD=EF。即将这个六位数拆成三个两位数,使得第1个和第2个两位数的和等于第3个两位数。 输入描述: 单组输入。输入两个六位正整数M和N(M 输出描述: 输出在M到N之间(包含M和N)满足要求的六位数的个数。 输入例子1: 100000 110000 输出例子1: 0 思路: 首先对m、n进行判断,当m比n小时,才能进入循环中。对在范围中的六位数不断对10取余,除10,分别得到a-f,最后满足ab+cd=ef条件,计数+1。 代码: 小美和小团合作开发了一款新游戏!他们相信这款游戏一定可以大火。 游戏规则是这样的,现在有一个方格地图,你控制一个机器人位于初始位置(x, y),然后你可以向上下左右的地块移动。其中一些地块上会有得分点,经过这些点可以获得分数。当然,路上还会有一些陷阱点,如果想要通过陷阱点,就需要付出一定的分数来清除这个陷阱点。注意陷阱点付出分数清除后就会变成普通的地块。即反复经过陷阱点只需付出一次代价。同样的,得分点也只能获得一次分数。 小美想到了一个策划案来让这个游戏变得难一些。小美把地图和机器人的初始位置给了小团,并且告诉了小团他操控机器人的行进路线。小美想试试小团能不能算出来他的最终得分。 小团完美地完成了这个任务。现在,小美和小团想找一些测试人员看看这款游戏的难度如何。他们找到了你,希望你帮他们测试一下这个游戏。而你能否挑战成功呢? 注意分数允许为负。初始分数为0. 输入描述: 第一行四个整数N,M,P,Q,表示这张地图是N行M列的,得分点的得分是P,陷阱点清除的代价是Q。接下来N行,每行M个字符,表示这张地图。其中,字符S表示初始机器人位置。字符#表示墙壁,字符O代表得分点。字符X代表陷阱点。字符+代表普通的地块。 接下来一行一个连续的字符串表示机器人的移动路线,只由大写字母WASD构成,W向上,A向左,S向下,D向右。机器人可以上下左右移动。不能超出地图边界。也不能走到墙壁之上。试图走出边界和走到墙壁的行动会停留在原来的位置不动。 输出描述: 一个整数,表示小团的机器人最终获得了多少分 输入例子1: 6 6 20 10 S#++O# OXX#X# ++++++ ###XX# ++#O#+ OXO++X SSDDDDDAWWSSSAWSSSADDD 输出例子1: 40 思路: 参考评论区中刷完这题我就去睡觉的思路: 写一个计分函数,当遇到得分点O,加上p,恢复成普通地块;当遇到陷阱点X,减去q,恢复成普通地块。 使用数据对表示当前位置,当可以前进时,向对应方向前进,计算更新当前分数,再次行进。 代码: 小美将自己朋友的名字写在了一块,惊讶地发现她写出的那个字符串S有一个惊人的性质:一个人是小美的朋友当且仅当她/他的名字是那个字符串的子序列。现在小团想根据那个字符串判断一个人是不是小美的朋友。 *子序列:一个字符串A是另外一个字符串B的子序列,当且仅当可以通过在B中删除若干个字符(也可能一个都不删),其他字符保留原来顺序,使得形成的新字符串B’与A串相等。例如,ABC是AABDDC的子序列,而ACB不是AABDDC的子序列。 输入描述: 第一行两个正整数n,m分别表示小美朋友字符串S的长度,以及小团想了解是不是小美朋友的那个人的名字字符串T的长度。 第二行长度为n且仅包含小写字母的字符串S. 第三行长度为m且仅包含小写字母的字符串T. 输出描述: 如果小团想了解的那个人不是小美的朋友(即,T不是S的子序列),输出一行”No”,否则输出一行”Yes”,并在第二行将T对应到S中的位置之和输出出来(从1开始编号),由于可能有多种对应方式,请输出最小的位置之和。请参见样例解释获取更详细说明 输入例子1: 6 3 aabddc abc 输出例子1: Yes 例子说明1: 对于样例1 S=aabddc T=abc,T中a可以与S中第1个字符a对应起来,b可以与S中第3个字符b对应起来,c可以与S中第6个字符c对应起来,这样一来就找到了一个S中的子序列(仅保留第1、3、6个字符形成的子序列),使其与T相等。这种情况下,位置之和为1+3+6=10 还有一种方案,是S仅保留第2、3、6个字符形成的子序列abc,仍然满足与T相等的条件。但是位置之和为2+3+6=11,并不是位置之和最小的,因而输出第一种对应方案的位置之和:10 输入例子2: 6 3 aabddc acb 输出例子2: No 例子说明2: 对于样例2 可以保留S中的第1、3、6个字符,形成子序列abc,但于T串acb不相等,因为b、c位置并不对应。可以证明,没有任何一种取S子序列的方式,可以使得取出的子序列和T相等,因而输出No 思路: 对s和t字符串进行遍历,同时检查当前t中的字符是否与s中的字符相等,相等即把该字符在s中的下标+1后加入总和sum中。直到对s遍历完成,如果在t中所有的字符在s中都顺序出现,即输出Yes和总和sum,否则输出No。 代码: 小美在观察一棵美丽的无根树。 小团问小美:“小美,我考考你,如果我选一个点为根,你能不能找出子树大小不超过K的前提下,子树内最大值和最小值差最大的子树的根是哪个点?多个点的话你给我编号最小的那个点就行了。” 小美思索一番,说这个问题难不倒他。 输入描述: 第一行两个正整数N和K,表示全树有N个节点,要求子树大小不超过K。第二行是N个正整数空格分隔,表示每个点的点权。以点编号从1到N的顺序给出点权。接下来N-1行每行两个正整数表示哪两个点之间有边相连。最后一行一个正整数root表示小团所选的根节点编号为root。 输出描述: 一行,一个正整数,含义如问题描述,输出在子树大小不超过K的前提下,子树内最大值和最小值差最大的子树的根的编号 输入例子1: 5 2 1 3 2 4 5 1 2 2 3 3 4 4 5 3 输出例子1: 2 思路: 参考评论区中零葬的思路: 用邻接表构建一个图,从小团给的根节点开始对图进行深搜。 (1) 以当前节点为根的子树有多少个节点; (2) 以当前节点为根的子树的最大叶权值和最小叶权值; (3) 截止到目前为止,子树权值极差的最大值; (4) 这个最大值对应的最小根节点编号。 得到以上信息后,利用(1)找到节点数不超过k的子树,如果子树的权值极差打破记录,就更新最大权值差和节点;如果等于记录,就看当前子树的根节点编号是否更小;小于记录就直接略过。 以下附上大佬Java代码: 代码: 第一题: 如何得到一个数的各个位数? 从个位开始不断地对其求对10求余的余数,然后除10,减少位数,直到除了相应的位数次数的10。 如求六位数的各个位数,就需要从个位开始,将其对10取余、除10,重复六次。 第二题: 函数参数传递的三种情况: 将变量名作为实参和形参 这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。 传递变量的指针(*) 形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。 这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量的地址而已。通过形参指针变量访问主函数中的变量,并改变它们的值。这样就能得到正确结果,但是在概念上却是兜了一个圈子,不那么直截了当。 传送变量的别名(&) 实际上,在虚实结合时是把实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。同样,将实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。这就是地址传递方式。 为便于理解,可以通俗地说:把变量的名字传给引用变量,使引用变量成为变量的别名。 C++ STL中的 之前的总结中已经说过cin/cout比scanf/print慢,因为要先把要输出的东西存入缓冲区,再输出,而使用 在默认的情况下 第三题: C++ string获取字符串元素: 字符串中元素的访问是允许的,一般可使用两种方法访问字符串中的单一字符:下标操作符[] 和 成员函数at()。两者均返回指定的下标位置的字符。第 1 个字符索引(下标)为 0,最后的字符索引为 length()-1。 两种访问方法的区别:(注意:AB、CD和EF都必须是正常的两位数,因此A、C和E都不能等于0。)
#include
小美的新游戏
#include
小美找朋友
10
#include
小美的美丽树
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.ArrayList;
public class Main {
static boolean[] visited; // 标记节点是否已经被访问
static int[] weight; // 节点权值
static int[] childNum; // 存储以节点i为根的树有多少个节点
static int[] max, min; // 存储以节点i为根的子树下的最大值和最小值
// 节点间的最大差值
static int maxDiff = -1;
// 待求节点
static int node = -1;
// 邻接表
static HashMap<Integer, ArrayList<Integer>> tree;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] temp = br.readLine().trim().split(" ");
int n = Integer.parseInt(temp[0]);
int k = Integer.parseInt(temp[1]);
temp = br.readLine().trim().split(" ");
weight = new int[n + 1];
for(int i = 1; i <= n; i++) weight[i] = Integer.parseInt(temp[i - 1]);
int x, y;
// 构建树图的邻接表
tree = new HashMap<>();
ArrayList<Integer> list;
for(int i = 1; i <= n - 1; i++){
temp = br.readLine().trim().split(" ");
x = Integer.parseInt(temp[0]);
y = Integer.parseInt(temp[1]);
if(tree.containsKey(x)){
list = tree.get(x);
list.add(y);
tree.put(x, list);
}else{
list = new ArrayList<>();
list.add(y);
tree.put(x, list);
}
if(tree.containsKey(y)){
list = tree.get(y);
list.add(x);
tree.put(y, list);
}else{
list = new ArrayList<>();
list.add(x);
tree.put(y, list);
}
}
int root = Integer.parseInt(br.readLine().trim());
visited = new boolean[n + 1];
max = new int[n + 1];
min = new int[n + 1];
childNum = new int[n + 1];
dfs(root, k);
System.out.println(node);
}
// 求取节点parent下子节点的最值
private static void dfs(int parent, int k) {
visited[parent] = true;
// 初始化parent下的最值为parent的节点权重
max[parent] = weight[parent];
min[parent] = weight[parent];
// 初始情况下,该子树只有一个节点
childNum[parent] = 1;
for(int i = 0; i < tree.get(parent).size(); i++){
int child = tree.get(parent).get(i);
if(!visited[child]){
// 没访问过这个孩子节点就进行深搜
dfs(child, k);
max[parent] = Math.max(max[parent], max[child]);
min[parent] = Math.min(min[parent], min[child]);
childNum[parent] += childNum[child];
}
}
if(childNum[parent] <= k && max[parent] - min[parent] >= maxDiff){
// 以parent为根节点的子树满足节点数小于等于k,且最大差值大于等于目前最大
if(max[parent] - min[parent] > maxDiff){
// 大于了直接更新,等于的话需要考虑哪个根节点的编号小
node = parent;
maxDiff = max[parent] - min[parent];
}else{
// 如果node还没有赋值,就直接赋值为当前节点,否则取满足要求的节点中编号最小的
node = node == -1? parent: Math.min(node, parent);
}
}
}
}
总结
queue
:队列,先进先出
#include
queue
对象:queue
q.push(x);
将x 接到队列的尾端。q.pop();
弹出队列的第一个元素,注意,并不会返回被弹出元素的值。q.front();
即最早被压入队列的元素q.back();
即最后被压入队列的元素。q.empty();
当队列空时,返回true。q.size();
ios::sync_with_stdio(false);
和 cin.tie(0)
加速C++输入输出流
std::ios::sync_with_stdio(false);
这句代码可以来打消iostream的输入输出缓存,节省许多时间,使效率与scanf/printf相差无几。tie
是将两个stream绑定的函数,空参数的话返回当前的输出流指针。这个函数是一个“是否兼容stdio”的开关。C++为了兼容C,保证程序在使用了std::printf
和std::cout
的时候不发生混乱,将输出流绑到了一起。cin
绑定的是cout
,每次执行 <<
操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)
(0表示NULL)来解除cin
与cout
的绑定,进一步加快执行效率。
[]
和at()
[]
在使用时不检查索引的有效性,如果下标超出字符的长度范围,会导致未定义行为。对于常量字符串,使用下标操作符时,字符串的最后字符(即 ‘\0’)是有效的。对应 string 类型对象(常量型)最后一个字符的下标是有效的,调用返回字符 ‘\0’。at()
在使用时会检查下标是否有效。如果给定的下标超出字符的长度范围,系统会抛出 out_of_range
异常。