目录
7-1 Lily(C++)
题目描述:
百合花(Lily)是一种美丽的花。她通常一年只开一次花,所以如果你看到百合花盛开,它会非常珍贵。然而,她对猫有剧毒,所以你必须注意让好奇的猫远离可爱的百合花。
你有n个网格的土壤土地排成一行,从1到n,其中一些是百合花。我们不想伤害百合,也不想伤害猫。你可以在网格上放一些猫粮,但对于任何有猫粮的网格i,在区域[i−1,i+1]不得含有百合花。你喜欢猫和百合,所以你想最大限度地增加有猫粮的格子。
设计满足上述要求的计划。
输入格式:
有一个整数n(1≤n≤1000)表示网格的数量。
第二行包含仅由“L”和“.”组成的字符串R,表示有和没有百合花的格子。
输出格式:
输出包含一行,字符串R′仅由“L”、“”组成和“C”,其中“C”表示在满足上述要求的同时分配给R中空网格的猫粮。
输入样例:
在这里给出一组输入。例如:
5
..L..
输出样例:
在这里给出相应的输出。例如:
C.L.C
解题思路:
先读入格子的状态
再遍历每一个格子,每到达一个空格子,检测其相邻格子中是否有有百合花
条件均符合则输出’C’,否则输出当前状态
代码实现如下
#include <iostream>
#include <vector>
using namespace std;
vector<char>boxes;
int main()
{
//I
char temp_char;
int n;
cin >> n;
boxed.reserve(n + 2);
boxes.push_back('.');//前冗余位
for (int i = 0; i < n; i++)
{
cin >> temp_char;
boxes.push_back(temp_char);
}
boxes.push_back('.');//后冗余位
//P and O
for (int i = 1; i <= n; i++)
{
if (boxes[i] == '.' && boxes[i - 1] != 'L' && boxes[i + 1] != 'L')
{
cout << 'C';
}
else
{
cout << boxes[i];
}
}
return 0;
}
7-2 a \ b(C++,数组模拟高精度)
题目描述:
给出两个不超过1000位的十六进制数a,b。
求a∗b的值
输入格式:
输入共两行,两个十六进制的数
输出格式:
输出一行,表示a∗b
输入样例:
在这里给出一组输入。例如:
1BF52
1D4B42
输出样例:
在这里给出相应的输出。例如:
332FCA5924
解题思路:
用代码对a*b进行模拟,需要明确两个步骤
(1)从a的个位开始,用a的每一位去乘以b的每一位
(2)每做一次运算进行一次进位操作
根据(1),我们需要两层for循环,外层为a,内层为b
实现时,会发现主要有两个问题
(1)如何实现进位
(2)如何存储乘积
大致的思路如下
for (int i = 最后一位; i >= 最高位; i--){
for(int j = 最后一位; j >= 最高位; j--){
int 进位 = (a[i] * b[j] + ab[i + j + 1]) / 16;
int 当前位 = (a[i] * b[i] + ab[i + j + 1]) % 16;
}
}
解决了这两个问题,我们就可以解决这道题了
完整的代码实现如下
#include <iostream>
#include <string>
using namespace std;
const int a_len = int(1e7);
int num_array[2][a_len] = { 0 };//两个操作数
int sum_array[a_len * 2] = { 0 };//乘积
int read(char c){//快读
if (c < '0' || c > '9')
return c - 'A' + 10;
else
return c - '0';
}
void output(int i){//快写
if (i > 9)
putchar(char(i + 55));
else
putchar(char(i + '0'));
}
int main(){
string str[2];
int len[2] = { 0 };
for (int i = 0; i < 2; i++){
cin >> str[i];
len[i] = int(str[i].size());
for (int j = a_len - len[i]; j <= a_len - 1; j++) {//个位对齐,从高位开始读入
num_array[i][j] = read(str[i][j - a_len + len[i]]);
}
}
int temp_num_1, temp_num_2;//用于进位操作
//乘法操作是效能的瓶颈
int i = 0, j = 0;
for (i = a_len - 1; i >= a_len - len[0]; i--){//从个位开始
for (j = a_len - 1; j >= a_len - len[1]; j--){//从个位开始
temp_num_1 = (num_array[0][i] * num_array[1][j] + sum_array[i + j + 1]) / 16;//进位
temp_num_2 = (num_array[0][i] * num_array[1][j] + sum_array[i + j + 1]) % 16;//当前位
sum_array[i + j] += temp_num_1;
sum_array[i + j + 1] = temp_num_2;
}
}
int index = i + j + 2;//+2平衡for循环尾处理
if (sum_array[index] == 0)//判断最高位是否有进位
++index;
while (index <= 2 * a_len - 1)
output(sum_array[index++]);
return 0;
}
7-3 山头狙击战(C++,二分法,map)
题目描述
小明为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次小明携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。小明是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,小明会等待敌人靠近然后射击。
正当小明为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时小明无法将他击毙,那么我们可怜的小明就将牺牲在敌人的刺刀下。现在小明用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,小明最多只能用多少时间给枪装上一发子弹?
说明:假设一开始小明的枪中就有一发子弹,并且一旦确定一个装弹时间,小明始终会用这个时间完成子弹的装卸。希望你能帮助小明脱离险境。
输入格式
每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表小明的射程。
接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。
输出格式
每组输出数据仅有一个整数,代表小明的换弹时间(单位为秒)。
样例输入
6 100
236
120
120
120
120
120
样例输出
25
解题思路:
一看到关于换弹时间的要求:在尽量长的同时不能超过一个值,就可以想到用二分法
考虑到题中并未说明输入的数据是有序的,我们需要输入数据进行排序
那么采用什么方法排序呢,是输入和排序同时进行?还是利用数组加sort()函数?
暂时无法确定
那么首先,二分法的实现如下
int left = 0, right = int(1e7) + 1, middle;
while (left + 1 != right)
{
middle = (left + right) / 2;
if (is_die(middle))//小明成功存活,判断换弹时间是否能够更长
{
max_time = middle;
left = middle;
}
else//小明被做掉了,换弹应该快一些
{
right = middle;
}
}
然后剩下的问题就是实现一个函数用于middle的判断了
参数列表:唯一需要传入的参数即为middle也就是换弹时间
返回值类型:需要返回bool类型值,因为返回值的作用只是判断接下来left = middle还是right = middle
本题的核心在于函数体的实现
先理解题意,什么样的换弹时间符合条件
换弹时间应该让小明能够存活下来,那么怎么判断小明是否能够存活
首先我们会建立这样一个模型,小明坐标始终为0,所有敌人不断前进,判断最近的敌人是否能够杀死小明
我们发现了,这里也同样要求敌人的位置是有序的,我们同样之后再说明
如何实现所有敌人不断前进?去改变所有敌人的坐标显然不是一个明智的选择
那么我们先说明第一种方法
用一个变量去累计时间,如果不是最近的敌人,先让他不要动,等轮到他了,他就直接瞬移一段距离
这种方法可行,但这里不予以实现,我们接下来要说明第二种方法
问题要抓住本质,本质上就是小明和敌人之间的距离不断变小的过程
那么换弹时间就可以转化为距离的变化,同样,不需要改变敌人的位置,我们可以改变小明的位置
那么现在题意完成了转化:
现在有一堆静止的靶子,小明每走一段距离才能开一枪
那么现在进一步考虑如何判断小明是否会遇到靶子的问题
为了避免TLE,我们首先考虑,能否一次性计算小明能否存活?
射程就是一个问题,那么再进一步,能否一次性计算射程内的所有敌人所需要的最短换弹时间?
如果1个敌人在int(1e7)处,int(1e5)-1个敌人就在1处,显然我们不需要考虑最远的那个敌人,只要能干掉面前的敌人,小明就能够成功存活
那么就只能采用遍历的方式了,我们现在来说明循环的实现
循环结束条件:当然就是敌人全部被干掉或者小明被干掉
然后说明循环体,我们需要考虑,射程内有没有敌人(当然这时候枪里有子弹,否则有没有敌人都是无意义的)
没有敌人的话,我们需要继续前进,直到有敌人进入射程
有敌人的话,我们需要判断小明能否存活
到这里了我们一定知道如何判断:小明干掉所有敌人需要前进的距离是否小于小明与敌人间的距离(为什么相等时小明能够存活呢,因为题中说小明能够瞬间干掉敌人)
不小于的话,那么由于换弹太慢,小明被干掉了
小于,敌人被干掉,我们继续下一轮循环
至此,函数实现完毕,以下是函数功能的实现
bool is_die(int bullet_time)//判断小明是否会die
{
//遍历enemy_map
int xm_index = 0;//小明的位置(注:每轮都需要初始化)
map<int, int>::iterator step = enemy_map.begin();
while (step != enemy_map.end())//干掉每一个敌人
{
if (step->first - xm_index > shoot_distance)//如果射程内没有敌人
{
xm_index += step->first - xm_index - shoot_distance;//向前进,直到敌人进入射程
}
else//如果射程内有敌人
{
if (step->first - xm_index < 0)//哦,原来敌人已经把我干掉了
{
//出现这种情况是因为在确认存活并换弹的过程中,有敌人把小明干掉了
return false;
}
if ((step->second - 1) * bullet_time <= step->first - xm_index)//如果小明不能走到敌人的面前
{
//注意在判断时,不用考虑杀死最后一个敌人后的换弹时间
xm_index += step->second * bullet_time;//确认存活,再换弹
step++;
}
else//如果小明会die
{
return false;
}
}
}
//循环退出,小明存活
return true;
}
到了这里,我想已经不用我去说怎么排序了,但还是做一下说明
排序的要求就是按照位置排序,每个位置要带有敌人数量的记录
其实就是存储并排序一对数据,那么很容易想到借助STL中的map
key存储位置,自动排序
value存储敌人数量
至此,本题实现完成
本题完整的代码如下
#include <iostream>
#include <map>
using namespace std;
map<int, int> enemy_map;//key用于存储敌人位置,value用于存储该位置的敌人数量
int shoot_distance;//射程
bool is_die(int bullet_time)//判断小明是否会die
{
//遍历enemy_map
int xm_index = 0;//小明的位置(注:每轮都需要初始化)
map<int, int>::iterator step = enemy_map.begin();
while (step != enemy_map.end())//干掉每一个敌人
{
if (step->first - xm_index > shoot_distance)//如果射程内没有敌人
{
xm_index += step->first - xm_index - shoot_distance;//向前进,直到敌人进入射程
}
else//如果射程内有敌人
{
if (step->first - xm_index < 0)//哦,原来敌人已经把我干掉了
{
//出现这种情况是因为在确认存活并换弹的过程中,有敌人把小明干掉了
return false;
}
if ((step->second - 1) * bullet_time <= step->first - xm_index)//如果小明不能走到敌人的面前
{
//注意在判断时,不用考虑杀死最后一个敌人后的换弹时间
xm_index += step->second * bullet_time;//确认存活,再换弹
step++;
}
else//如果小明会die
{
return false;
}
}
}
//循环退出,小明存活
return true;
}
int main()
{
int max_time = 0;//存储最大时间
int n;
cin >> n >> shoot_distance;//读入敌人个数、射程
int temp_key;//用于读入敌人的位置
map<int, int>::iterator is_exist;
for (int i = 0; i < n; i++)//读取敌人信息
{
cin >> temp_key;
is_exist = enemy_map.find(temp_key);
if (is_exist == enemy_map.end())//若当前位置没有敌人
{
enemy_map.insert(make_pair(temp_key, 1));
}
else//当前位置有敌人
{
is_exist->second += 1;//敌人数量+1
}
}
int left = 0, right = int(1e7) + 1, middle;
while (left + 1 != right)
{
middle = (left + right) / 2;
if (is_die(middle))//小明成功存活,判断是否换弹时间能够更长
{
max_time = middle;
left = middle;
}
else//小明被做掉了,换弹应该快一些
{
right = middle;
}
}
cout << max_time << endl;
return 0;
}
7-4 Reversing Linked List(C++,数组链表)
题目描述:
Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.
Input Specification:
Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤105) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.
Then N lines follow, each describes a node in the format:
Address Data Next
where Address
is the position of the node, Data
is an integer, and Next
is the position of the next node.
Output Specification:
For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.
Sample Input:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
Sample Output:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
解题思路:
题目大意:先读入n个节点(注意节点未必全部接入链表中),再反转链表,每次反转k个节点,若长度不足则不反转,最后输出反转后的链表
由于只有五位数,我们可以利用数组存储链表,以下标代表地址
本题核心在于链表的反转,接下来对此进行说明
首先是节点的读入,创建一个节点类型如下
struct node//节点类型
{
string cur_postion;//当前地址
string next_position;//下一地址
int value;//值
};
然后创建一个下标范围0~99999的节点数组
读入所有的节点,由于可以根据下标访问,链表已经连接完成
那么接下来除去多余的节点,同时统计有效长度
int_head = stoi(str_head);
while (int_head != -1){//将链表按顺序push,操作结束后已除去未连接起来的节点
vector_node_list.push_back(node_list[int_head]);
int_head = stoi(node_list[int_head].next_position);
len++;//统计有效长度
}
最后,**利用reverse()**很容易就实现了链表的反转
for (int i = 0; i < len; i += k) {
if (i + k > len) break;//剩余不足k个节点,break
reverse(vector_node_list.begin() + i, vector_node_list.begin() + i + k);//反转k个节点(反转后next_pos失效)
}
最后根据题意输出即可
vector<node>::iterator step = vector_node_list.begin();
cout << step->cur_postion << ' ' << step->value << ' ';//第一行前两个数据单独输出
while (++step != vector_node_list.end())
cout << step->cur_postion << '\n' << step->cur_postion << ' ' << step->value << ' ';//输出前一行的最后一个数据和本行的前两个数据
cout << -1;//输出最后一行的最后一个数据
本题的完整代码如下
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct node//节点类型
{
string cur_postion;//当前地址
string next_position;//下一地址
int value;//值
};
//用数组的下标来模拟地址
struct node node_list[int(1e5)];//存储所有节点
vector<node> vector_node_list;//去除多余节点后的链表
int main(){
string str_head, cur_position, next_position;//首地址,用于读入cur_pos的变量、用于读入next_pos的变量
int int_head, n, k, value, len = 0;
cin >> str_head >> n >> k;//读入首地址,节点数,反转的节点数
for (int i = 0; i < n; i++){
cin >> cur_position >> value >> next_position;
node_list[stoi(cur_position)].cur_postion = cur_position;
node_list[stoi(cur_position)].value = value;
node_list[stoi(cur_position)].next_position = next_position;
}//读入结束链表已连接完毕
int_head = stoi(str_head);
while (int_head != -1){//将链表按顺序push,操作结束后已除去未连接起来的节点
vector_node_list.push_back(node_list[int_head]);
int_head = stoi(node_list[int_head].next_position);
len++;//统计有效长度
}
for (int i = 0; i < len; i += k) {
if (i + k > len) break;//剩余不足k个节点,break
reverse(vector_node_list.begin() + i, vector_node_list.begin() + i + k);//反转k个节点(反转后next_pos失效)
}
vector<node>::iterator step = vector_node_list.begin();
cout << step->cur_postion << ' ' << step->value << ' ';//第一行前两个数据单独输出
while (++step != vector_node_list.end())
cout << step->cur_postion << '\n' << step->cur_postion << ' ' << step->value << ' ';//输出前一行的最后一个数据和本行的前两个数据
cout << -1;//输出最后一行的最后一个数据
return 0;
}
7-5 一元三次方程(C++,二分法)
题目描述:
给定一个形如 a x 3 + b x 2 + c x + d = 0 ax^3+bx^2+cx+d=0 ax3+bx2+cx+d=0的一元三次方程。
已知该方程有三个不同的实数根(根与根之差的绝对值≥ 1 0 − 6 10^{−6} 10−6),且根范围均在[p,q]之间,你需要解出这个方程的三个根。
输入格式:
第一行一个整数T(1≤T≤1000),表示有T组数据
接下来T行,每行6个实数,分别表示a,b,c,d,p,q
数据保证: − 1 0 2 −10^2 −102≤p,q≤ 1 0 2 10^2 102,且对于∀x∈[p,q], − 1 0 6 −10^6 −106≤f(x)≤ 1 0 6 10^6 106
输出格式:
输出三个实数,表示方程的三个解。
你的答案可以以任意顺序输出。
一个答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过 1 0 − 6 10^{−6} 10−6
输入样例:
在这里给出一组输入。例如:
1
1.000000 -5.000000 -4.000000 20.000000 -10.000000 10.000000
输出样例:
在这里给出相应的输出。例如:
-2.000000 2.000000 5.000000
提示1:
样例所给方程的图像如下:
提示2:
对于方程: a x 2 + b x + c = 0 ax^2+bx+c=0 ax2+bx+c=0( Δ = b 2 − 4 a c ≥ 0 Δ=b^2−4ac≥0 Δ=b2−4ac≥0),它的两个根分别为: x 1 = − b − Δ 2 a x_1=\frac{−b−\sqrtΔ}{2a} x1=2a−b−Δ, x 2 = − b + Δ 2 a x_2=\frac{-b+\sqrtΔ}{2a} x2=2a−b+Δ
解题思路:
(1)求导解二次方程确定三个根的范围
(2)利用二分法求解
代码实现如下:
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
//计算三次方程的y值
double three_calculate(double x, double a = 0.0, double b = 0.0, double c = 0.0, double d = 0.0){//传入x和参数
return a * x * x * x + b * x * x + c * x + d;
}
//二分搜索根的功能实现
double search_solution(double left, double right, bool select, double a = 0.0, double b = 0.0, double c = 0.0, double d = 0.0){//传入范围,递增/递减,参数
double inner_middle = (left + right) / 2;
if (select){//select = true为递增
while (right - left > 1e-7){//停止条件要精确到下一位
inner_middle = (left + right) / 2;
if (three_calculate(inner_middle, a, b, c, d) > 0)
right = inner_middle;
else
left = inner_middle;
}
}
else{//select = false为递减
while (right - left > 1e-7){//停止条件要精确到下一位
inner_middle = (left + right) / 2;
if (three_calculate(inner_middle, a, b, c, d) > 0)
left = inner_middle;
else
right = inner_middle;
}
}
return inner_middle;
}
//求导解二次方程两根,定范围
void define_range(double* m, double* n, bool select, double a, double b, double c){//传入保存两根的地址,参数
a *= 3, b *= 2, c *= 1;
double dert = sqrt(b * b - 4 * a * c);
if (select){//注意a的符号
*m = (-b - dert) / (2 * a);
*n = (-b + dert) / (2 * a);
}
else{
*n = (-b - dert) / (2 * a);
*m = (-b + dert) / (2 * a);
}
}
int main(){
int T;
double a, b, c, d, p, q, m, n;
bool select;
cin >> T;
for (int i = 0; i < T; i++){
cin >> a >> b >> c >> d >> p >> q;
if (a > 0)//判断a的符号
select = true;
else
select = false;
define_range(&m, &n, select, a, b, c);//要求m>n
cout << setiosflags(ios::fixed) << setprecision(6) << search_solution(p , m, select, a, b, c, d) << ' ' << search_solution(m, n, !select, a, b, c, d)
<< ' ' << search_solution(n, q, select, a, b, c, d) << endl;
}
return 0;
}