点此前往练习
小团想要编写一个程序,希望可以统计在M和N之间(M
(1) ABCDEF这六个数字均不相同,即A、B、C、D、E和F表示六个不同的数字。
(2) AB+CD=EF。即将这个六位数拆成三个两位数,使得第1个和第2个两位数的和等于第3个两位数。
(注意:AB、CD和EF都必须是正常的两位数,因此A、C和E都不能等于0。)
输入描述:
单组输入。输入两个六位正整数M和N(M
输出描述:
输出在M到N之间(包含M和N)满足要求的六位数的个数。
输入例子1:
100000 110000
输出例子1:
0
思路:
首先对m、n进行判断,当m比n小时,才能进入循环中。对在范围中的六位数不断对10取余,除10,分别得到a-f,最后满足ab+cd=ef条件,计数+1。
代码:
#include
using namespace std;
int main()
{
int m,n;
cin>>m>>n;
int count=0;
if(m<n&&m/100000>=1&&n/100000>=1)
{
for(int i=m;i<=n;i++)
{
int tmp=i;
int f=tmp%10;
tmp/=10;
int e=tmp%10;
if(e==f)
continue;
tmp/=10;
int d=tmp%10;
if(d==e||d==f)
continue;
tmp/=10;
int c=tmp%10;
if(c==d||c==e||c==f)
continue;
tmp/=10;
int b=tmp%10;
if(b==c||b==d||b==e||b==f)
continue;
tmp/=10;
int a=tmp%10;
if(a==b||a==c||a==d||a==e||a==f)
continue;
tmp/=10;
if((a*10+b)+(c*10+d)==(e*10+f))
count++;
}
}
cout<<count;
return 0;
}
小美和小团合作开发了一款新游戏!他们相信这款游戏一定可以大火。
游戏规则是这样的,现在有一个方格地图,你控制一个机器人位于初始位置(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,恢复成普通地块。
使用数据对表示当前位置,当可以前进时,向对应方向前进,计算更新当前分数,再次行进。
代码:
#include
using namespace std;
void Score(int i,int j,int &score,vector<vector<char>> &a,int p,int q)
{
if(a[i][j]=='O')
{
score+=p;
a[i][j]='+';
}
if(a[i][j]=='X')
{
score-=q;
a[i][j]='+';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int m,n,p,q;
cin>>n>>m>>p>>q;
vector<vector<char>> a;
pair<int,int> pos={0,0};
int score=0;
for(int i=0;i<n;i++)
{
a.push_back(vector<char>());
for(int j=0;j<m;j++)
{
char c;
cin>>c;
if(c=='S')
pos={i,j};
a[i].push_back(c);
}
}
string s;
cin>>s;
queue<pair<int,int>> qq;
qq.push(pos);
for(int i=0;i<s.size();i++)
{
auto node=qq.front();
int row=node.first,col=node.second;
if(s[i]=='W')
{
if(row-1<0||a[row-1][col]=='#')
continue;
else
{
qq.push({row-1,col});
Score(row-1,col,score,a,p,q);
qq.pop();
}
}
if(s[i]=='A')
{
if(col-1<0||a[row][col-1]=='#')
continue;
else
{
qq.push({row,col-1});
Score(row,col-1,score,a,p,q);
qq.pop();
}
}
if(s[i]=='S')
{
if(row+1>=n||a[row+1][col]=='#')
continue;
else
{
qq.push({row+1,col});
Score(row+1,col,score,a,p,q);
qq.pop();
}
}
if(s[i]=='D')
{
if(col+1>=m||a[row][col+1]=='#')
continue;
else
{
qq.push({row,col+1});
Score(row,col+1,score,a,p,q);
qq.pop();
}
}
}
cout<<score;
return 0;
}
小美将自己朋友的名字写在了一块,惊讶地发现她写出的那个字符串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
10
例子说明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。
代码:
#include
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
string s,t;
long sum=0;
int j=0;
cin>>s;
cin>>t;
for(int i=0;i<n;i++)
{
if(j<m&&t[j]==s[i])
{
j++;
sum+=i+1;
}
}
if(j==t.size())
{
cout<<"Yes"<<endl;
cout<<sum<<endl;
}
else
cout<<"No"<<endl;
return 0;
}
小美在观察一棵美丽的无根树。
小团问小美:“小美,我考考你,如果我选一个点为根,你能不能找出子树大小不超过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代码:
代码:
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);
}
}
}
}
第一题:
如何得到一个数的各个位数?
从个位开始不断地对其求对10求余的余数,然后除10,减少位数,直到除了相应的位数次数的10。
如求六位数的各个位数,就需要从个位开始,将其对10取余、除10,重复六次。
第二题:
函数参数传递的三种情况:
将变量名作为实参和形参
这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。
传递变量的指针(*)
形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。
这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量的地址而已。通过形参指针变量访问主函数中的变量,并改变它们的值。这样就能得到正确结果,但是在概念上却是兜了一个圈子,不那么直截了当。
传送变量的别名(&)
实际上,在虚实结合时是把实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。同样,将实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。这就是地址传递方式。
为便于理解,可以通俗地说:把变量的名字传给引用变量,使引用变量成为变量的别名。
C++ STL中的queue
:队列,先进先出
#include
queue
对象:queue q;
q.push(x);
将x 接到队列的尾端。q.pop();
弹出队列的第一个元素,注意,并不会返回被弹出元素的值。q.front();
即最早被压入队列的元素q.back();
即最后被压入队列的元素。q.empty();
当队列空时,返回true。q.size();
ios::sync_with_stdio(false);
和 cin.tie(0)
加速C++输入输出流
之前的总结中已经说过cin/cout比scanf/print慢,因为要先把要输出的东西存入缓冲区,再输出,而使用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
的绑定,进一步加快执行效率。
第三题:
C++ string获取字符串元素:[]
和at()
字符串中元素的访问是允许的,一般可使用两种方法访问字符串中的单一字符:下标操作符[] 和 成员函数at()。两者均返回指定的下标位置的字符。第 1 个字符索引(下标)为 0,最后的字符索引为 length()-1。
两种访问方法的区别:
[]
在使用时不检查索引的有效性,如果下标超出字符的长度范围,会导致未定义行为。对于常量字符串,使用下标操作符时,字符串的最后字符(即 ‘\0’)是有效的。对应 string 类型对象(常量型)最后一个字符的下标是有效的,调用返回字符 ‘\0’。at()
在使用时会检查下标是否有效。如果给定的下标超出字符的长度范围,系统会抛出 out_of_range
异常。