NEUQACM双周赛(一)

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
ab的值

输入格式:

输入共两行,两个十六进制的数

输出格式:

输出一行,表示ab

输入样例:

在这里给出一组输入。例如:

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} 106),且根范围均在[p,q]之间,你需要解出这个方程的三个根。

输入格式:

第一行一个整数T(1≤T≤1000),表示有T组数据

接下来T行,每行6个实数,分别表示a,b,c,d,p,q

数据保证: − 1 0 2 −10^2 102p,q 1 0 2 10^2 102,且对于∀x∈[p,q], − 1 0 6 −10^6 106f(x)≤ 1 0 6 10^6 106

输出格式:

输出三个实数,表示方程的三个解。

你的答案可以以任意顺序输出。

一个答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过 1 0 − 6 10^{−6} 106

输入样例:

在这里给出一组输入。例如:

1
1.000000 -5.000000 -4.000000 20.000000 -10.000000 10.000000

输出样例:

在这里给出相应的输出。例如:

-2.000000 2.000000 5.000000

提示1:

样例所给方程的图像如下:

T4.png

提示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 Δ=b24ac0),它的两个根分别为: x 1 = − b − Δ 2 a x_1=\frac{−b−\sqrtΔ}{2a} x1=2abΔ , x 2 = − b + Δ 2 a x_2=\frac{-b+\sqrtΔ}{2a} x2=2ab+Δ

解题思路:

(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值