百练200题总结

百练200题总结

1. 有趣的跳跃 = 充分利用数组下标+再进行排序遍历

题目描述

描述
一个长度为n(n>0)的序列中存在“有趣的跳跃”当前仅当相邻元素的差的绝对值经过排序后正好是从1到(n-1)。例如,1 4 2 3存在“有趣的跳跃”,因为差的绝对值分别为3,2,1。当然,任何只包含单个元素的序列一定存在“有趣的跳跃”。你需要写一个程序判定给定序列是否存在“有趣的跳跃”。

输入
一行,第一个数是n(0 < n < 3000),为序列长度,接下来有n个整数,依次为序列中各元素,各元素的绝对值均不超过1,000,000,000。
输出
一行,若该序列存在“有趣的跳跃”,输出"Jolly",否则输出"Not jolly"。

结题思路

思路1(暴力):

  1. 计算原序列中第1个到第n-1个元素与其后面一个元素的差值的绝对值,依次存放在数组中。
  2. 对数组进行排序,遍历数组是不是从1到n-1序列。
  3. 若遍历时,数组元素不等于自增量,则输出"Not jolly",并return 0;若遍历完后没有return 0,则输出"Jolly"。

思路2(充分利用数组下标):

  1. 定义一个数组a,初始化为0。
  2. 从输入序列的第二个元素开始,直到最后一个元素,计算每一个元素与上一个元素的绝对值,记为index,将a[index]赋值为1
  3. 对a数组排序,遍历数组a,看数组是否全部等于1。若不是,则输出"Not jolly",若是,则输出"Jolly"。

源代码

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
int main()
{
	int n;
	if(cin >> n)
	{
		long long a[n];
		long long b[n-1];
		for(int i = 0; i < n; i++)
		{
			cin >> a[i];
		}
		for(int i = 0; i < n-1; i++)
		{
			b[i] = abs(a[i+1] - a[i]);
		}
		sort(b, b+n-1);
		for(int i = 0; i < n-1; i++)
		{
			if(b[i] != i+1)
			{
				cout << "Not jolly" << endl;
				return 0;
			}
		}
		cout << "Jolly" << endl;
	}
	return 0;
}
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
	int n;
	int last, current;
	int a[3001] = {0};
	cin >> n;
	cin >> last;
	for(int i = 1; i < n; i++)
	{
		cin >> current;
		a[abs(current - last)]++;
		last = current;
	}
	for(int i = 1; i < n; i++)
	{
		//a数组若越界,也属于此类 
		if(a[i] != 1)
		{
			cout << "Not jolly" << endl;
			return 0;
		} 
	}
	cout << "Jolly" << endl;
	return 0;
}

我的看法

此题可暴力求解,也可充分利用数组的下标,只有满足相邻元素的差值正好是从1到(n-1)时,才能正常地遍历数组a,值越界或差不满足从1到(n-1)时,都会输出Not jolly。另外,方法1的复杂度为nlogn2,方法2的复杂度为n,方法2较快。

下次遇到此类题我要注意的地方

1.记录a[i]=1,充分利用数组下标,进行遍历,看value值是否都满足要求。

充分利用数组的下标

此类题模板代码

for(int i = 1; i < n; i++)
	{
		cin >> current;
		a[abs(current - last)]++;
		last = current;
	}

2. 玛雅历 = 两个独立循环 + 字符串比对函数

题目描述

描述

上周末,M.A. Ya教授对古老的玛雅有了一个重大发现。从一个古老的节绳(玛雅人用于记事的工具)中,教授发现玛雅人使用了一个一年有365天的叫做Haab的历法。这个Haab历法拥有19个月,在开始的18个月,一个月有20天,月份的名字分别是pop, no, zip, zotz, tzec, xul, yoxkin, mol, chen, yax, zac, ceh, mac, kankin, muan, pax, koyab, cumhu。这些月份中的日期用0到19表示。Haab历的最后一个月叫做uayet,它只有5天,用0到4表示。玛雅人认为这个日期最少的月份是不吉利的,在这个月法庭不开庭,人们不从事交易,甚至没有人打扫屋中的地板。

因为宗教的原因,玛雅人还使用了另一个历法,在这个历法中年被称为Tzolkin(holly年),一年被分成13个不同的时期,每个时期有20天,每一天用一个数字和一个单词相组合的形式来表示。使用的数字是1~13,使用的单词共有20个,它们分别是:imix, ik, akbal, kan, chicchan, cimi, manik, lamat, muluk, ok, chuen, eb, ben, ix, mem, cib, caban, eznab, canac, ahau。注意:年中的每一天都有着明确唯一的描述,比如,在一年的开始,日期如下描述: 1 imix, 2 ik, 3 akbal, 4 kan, 5 chicchan, 6 cimi, 7 manik, 8 lamat, 9 muluk, 10 ok, 11 chuen, 12 eb, 13 ben, 1 ix, 2 mem, 3 cib, 4 caban, 5 eznab, 6 canac, 7 ahau, ,8 imix, 9 ik, 10 akbal ……也就是说数字和单词各自独立循环使用。

Haab历和Tzolkin历中的年都用数字0,1,……表示,数字0表示世界的开始。所以第一天被表示成:
Haab: 0. pop 0
Tzolkin: 1 imix 0
请帮助M.A. Ya教授写一个程序可以把Haab历转化成Tzolkin历。

输入

Haab历中的数据由如下的方式表示:
日期. 月份 年数

输入中的第一行表示要转化的Haab历日期的数据量。下面的每一行表示一个日期,年数小于5000。

输出

Tzolkin历中的数据由如下的方式表示:
天数字 天名称 年数

第一行表示输出的日期数量。下面的每一行表示一个输入数据中对应的Tzolkin历中的日期。

结题思路

方法1:将Haab历转化为一年中的第多少天,然后再转成Tzolkin历。

  1. 定义数组分别存放两种历法的月份、日期和Haab历的天数。
  2. 根据输入数据的格式,将每行输入按照"%d. %s %d"的格式分别存到日,月,年中。按照两种日历的规则,将日月年按照计算规则转化成第多少天,再转化成Tzolkin历。(这里需要注意的是:Tzolkin历的每一天用一个数字和一个单词相组合的形式来表示,他们是各自独立循环使用的,即:每一天都是day为单位做循环,没有月份的说法,其只是一个数字的代表。)

源代码

#include <iostream>
#include <cstring>
using namespace std;
const int NAMELENGTH = 10;
int main()
{
	int n;
	int index;
	char HaabYue[19][NAMELENGTH] = {"pop", "no", "zip", "zotz", "tzec", "xul", "yoxkin", "mol", "chen", "yax", "zac", "ceh", "mac", "kankin", "muan", "pax", "koyab", "cumhu"};
	char TzolkinYue[20][NAMELENGTH] = {"imix","ik","akbal","kan","chicchan","cimi","manik","lamat","muluk","ok","chuen","eb","ben","ix","mem","cib","caban","eznab","canac","ahau"};	
	cin >> n;
	cout << n << endl;
	for(int j = 0; j < n; j++)
	{
		int days;
		int day, year;
		char month[NAMELENGTH];
		scanf("%d. %s %d", &day, month, &year);
		int i;
		for(i = 0; i < 19; i++)
		{
			if(!strcmp(HaabYue[i], month))
			{
				break;
			}
		}
		days = year * 365 + 20 * i + day;
		cout << days %  13 + 1 << " " << TzolkinYue[days % 20] << " " << days / 260 << endl;
	} 
	return 0;
}

意见

  1. 字符串比对函数strcmp():若两个字符串相等,则返回0。用于if()中时,用!才能表示进入。
  2. Tzolkin历相互不影响,独立循环,总共可以表示260天。

下次遇到此类题我要注意的地方

1.字符串比对函数strcmp()。
  1. 定义字符二维数组来解决字符串比对问题。
  2. 特别注意细节之处,尤其是月份是否从0开始,需要遍历多少次。
  3. 不知道为什么,我的代码不能AC,但是测试用几个用例都是能用的。需要得到解答!

此类题模板代码

  1. 充分利用数组下标和value。
for(int i = 1; i < n; i++)
	{
		cin >> current;
		a[abs(current - last)]++;
		last = current;
	}

3. 走迷宫 = DFS + 防止回走 + 防止越界

题目描述

描述
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。
输入
第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用’.‘表示,有障碍物的格子用’#‘表示。
迷宫左上角和右下角都是’.’。
输出
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。
样例输入

5 5
..###
#....
#.#.#
#.#.#
#.#..

样例输出
9

结题思路

方法1:深度优先搜索

  1. 输入迷宫的长和宽。
  2. 定义一个二维字符数组a[R][C]表示迷宫。
  3. 从a[0][0]出发,调用深度优先的函数,传入横纵坐标和步数。
  4. 在DFS函数中,判断此坐标为最右下角,若是,则与目前最少的步数比较,将两者的较小值赋值给minStep,并返回;若不是,则继续判断该坐标是否越界或为’#’,若满足则返回。
  5. 设置此坐标为’#’(可避免往回走,同时可以一个visit数组来避免往回走),对其上下左右方向进行DFS,传过去的步数是+1的。然后将此坐标恢复为’.’。
  6. 直到到达最右下角。

源代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
char a[40][40]; 
int r, c;
int count = 0;
int minStep = 10000;
int visit[41][41];
void DFS(int i, int j, int step)
{
	if(i == r-1 && j == c-1)
	{
		minStep = min(minStep, step);
		return;
	}
	if( i < 0 || i > r-1 || j < 0 || j > c-1 || a[i][j] == '#')
	{
		return;
	}
	a[i][j] = '#';
	DFS(i-1, j, step+1);
	DFS(i+1, j, step+1);
	DFS(i, j-1, step+1);
	DFS(i, j+1, step+1);
	a[i][j] = '.';
}
int main()
{
	cin >> r >> c;
	for(int i = 0; i < r; i++)
	{
		for(int j = 0; j < c; j++)
		{
			cin >> a[i][j];
		}
	}
	DFS(0, 0, 1);
	cout << minStep << endl;
	return 0;
}

题型分析

  1. 此题属于DFS,BFS类题,也是属于图类题。
  2. 以后要做一个搜索类题目的比较合集。
  3. 此题也可以用广度优先搜索做,目前还没有尝试。

下次遇到此类题我要注意的地方

1.DFS要注意不能返回走,可设置a[i][j] = ‘#’,同时也可以设置一个visit数组来避免往回走。2.strcmp用于比较字符数组或两个"aa"和"bb"是否相等。3. ==用来判断同类型变量是否相等。
  1. strcmp只能比较两个字符串是否相等,不能比较字符串和char类型(’'单引号表示char,双引号表示字符串)。
  2. == 只能用于判断两个同类型的变量是否相等。
  3. 在处理搜索类型题目时,要注意增加是否越界的条件

此类题模板代码

  1. DFS防止回走。
int visit[41][41];

或者

a[i][j] = '#';
	DFS(i-1, j, step+1);
	DFS(i+1, j, step+1);
	DFS(i, j-1, step+1);
	DFS(i, j+1, step+1);
	a[i][j] = '.';

4. 最大子序列和 = 只要是正数就相加

题目描述

结题思路

思路1:动态规划的方法:求出每个位置的目前最大子序列和

  1. 从a[0]开始,比较当前最大子序列的和与其加下一个数,赋值给ans[i],得到每一个位置的最大子序列和。
  2. ans数组中最大的那个值即是结果。

源代码

#include <iostream>
#include <stdlib.h>
#include <cmath>
using namespace std;

int main()
{
	int n;
	cin >> n;
	int *array, *ans;
	int cur_sum, max_sum;
	array = (int*)calloc(n+1, sizeof(int));
	for(int i = 0; i < n; i++)
	{
		cin >> array[i];
	}
	cur_sum = max_sum = array[0];
	for(int i = 1; i < n; i++)
	{
		cur_sum = cur_sum + array[i] > cur_sum ? cur_sum + array[i] : cur_sum;
		max_sum = max_sum > cur_sum ? max_sum : cur_sum;
	}
	cout << max_sum << endl;	
	return 0;
} 

题型分析

下次遇到此类题我要注意的地方

1.求出到每个位置为止最大子序列和。

5. 最大上升子序列和 = 如果当前值大于j位置的数,并且他们组成的上升序列和更大。则更新i位置的最大值 + 两层for循环 + if条件判断 + calloc(stdlib.h)

题目描述

描述

一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …,aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中序列和最大为18,为子序列(1, 3, 5, 9)的和.

你的任务,就是对于给定的序列,求出最大上升子序列和。注意,最长的上升子序列的和不一定是最大的,比如序列(100, 1, 2, 3)的最大上升子序列和为100,而最长上升子序列为(1, 2, 3)

输入

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000(可能重复)。

输出

最大上升子序列和

样例输入

7
1 7 3 5 9 4 8

样例输出

18

结题思路

思路1:动态规划的方法:找出第i个数(a)结尾的最大上升子串和。

  1. 需满足三个条件:在i之前的&&数组a中小于a[i]&&ans的最大值
    (1)下标在i之前。for(int j = i-1; j >= 0; j–)
    (2)a[j] < a[i] (上升序列)
    (3)ans的最大值。
  2. 此i之前的最大上升子串和加上a[i] (此题中a[i]是大于0的)。

思路2:如果当前值大于j位置的数,并且他们组成的上升序列和更大。则更新i位置的最大值。

源代码

#include <iostream>
#include <stdlib.h>
#include <cmath>
using namespace std;
int findMaxIncSum(int ans[], int i, int a[])//第i个数(a)结尾的最大子串和 
{
	int max = 0;
	for(int j = i-1; j >= 0; j--)//下标在i之前的&&数组a中小于a[i]的ans的最大值 
	{
		if(a[i] <= a[j])// 数组a中小于a[i]
		{
			continue;
		}
		else 
		{
			if(ans[j] > max)
			{
				max = ans[j];
			}
		}
	}
	return max + a[i];
}
int main()
{
	int n;
	cin >> n;
	int *array, *ans;
	int max_sum;
	array = (int*)calloc(n+1, sizeof(int));
	ans = (int*)calloc(n+1, sizeof(int)); 
	for(int i = 0; i < n; i++)
	{
		cin >> array[i];
	}	
	for(int i = 0; i < n; i++)
	{
		ans[i] = findMaxIncSum(ans, i, array);
	}
	max_sum = ans[0];
	for(int i = 1; i < n; i++)
	{
		max_sum = max_sum > ans[i] ? max_sum : ans[i];
	}	
	cout << max_sum << endl;
	return 0;
} 
#include<bits/stdc++.h> 
using namespace std;
int main(){
	int N,num[1010];
	int max[1010]; //存当前位置的最大上升序列的和 
	cin>>N;
	memset(max, 0, sizeof(max));
	for(int i = 0; i < N; ++i)	{
		cin>>num[i];
		max[i] = num[i];	//如果是非常大的数无法加入上升序列,可能其本身就是最优值 
	}

	for(int i = 1; i < N; ++i){
		for(int j = 0; j < i; ++j){
			if(num[i]>num[j]&&num[i]+max[j]>max[i]){	//如果当前值大于j位置的数,并且他们组成的上升序列和更大。则更新i位置的最大值 
					max[i] = num[i]+max[j];
			}
		}
	}
	int re = 0;
	for(int t:max)	re = re>t?re:t;		//从所有的序列最大值中找到最优解 
	cout<<re;
    return 0;
}

题型分析

  1. 动态规划题,需要找出到每个位置为止,a[j] < a[i] && 的最大上升子串之和。
  2. 此题中,a[i]是大于0的,若没有此限制条件,则在findMaxIncSum中首先要判断a[i]是否大于0,若小于则可以直接用上一个位置的最大上升子串和。
  3. 此类动态规划题的关键是:找到"num[i]>num[j]&&num[i]+max[j]>max[i]"此条件,满足此条件的则需要判断。

下次遇到此类题我要注意的地方

1.动态规划题,要特别注意改变某个位置最大值的条件
  1. 用calloc动态分配内存。
  2. 用动态规划算法求出每个时刻的最大值。

此类题模板代码


6. Yogurt factory = 要不要在上次生产时,多生产一些 + 用一个flag记录上一次生产的日期(并更新)

题目描述

描述

The cows have purchased a yogurt factory that makes world-famous Yucky Yogurt. Over the next N (1 <= N <= 10,000) weeks, the price of milk and labor will fluctuate weekly such that it will cost the company C_i (1 <= C_i <= 5,000) cents to produce one unit of yogurt in week i. Yucky’s factory, being well-designed, can produce arbitrarily many units of yogurt each week.
在接下来的N周,牛奶和劳力的价格会波动,公司在第i周生产每单位酸奶的存储费为C_i美分。公司每周可以生产任意单位的酸奶。
Yucky Yogurt owns a warehouse that can store unused yogurt at a constant fee of S (1 <= S <= 100) cents per unit of yogurt per week. Fortuitously, yogurt does not spoil. Yucky Yogurt’s warehouse is enormous, so it can hold arbitrarily many units of yogurt.
继续存储每单位酸奶每周花费S美分存储费,酸奶不会过期且能存无限量。
Yucky wants to find a way to make weekly deliveries of Y_i (0 <= Y_i <= 10,000) units of yogurt to its clientele (Y_i is the delivery quantity in week i). Help Yucky minimize its costs over the entire N-week period. Yogurt produced in week i, as well as any yogurt already in storage, can be used to meet Yucky’s demand for that week.
每周交付Y_i单位鲜奶,每周生产量i和存储量均可用于出售

输入

  • Line 1: Two space-separated integers, N and S.
    N周 S美分
  • Lines 2…N+1: Line i+1 contains two space-separated integers: C_i and Y_i.
    i周的存储费C_i美分 每周交付量Y_i

输出

  • Line 1: Line 1 contains a single integer: the minimum total cost to satisfy the yogurt schedule. Note that the total might be too large for a 32-bit integer.
    制定最好的生产计划,当交付Y_i时,总花费最少

样例输入

4 5
88 200
89 400
97 300
91 500

样例输出

126900

提示

OUTPUT DETAILS:

In week 1, produce 200 units of yogurt and deliver all of it. In week 2, produce 700 units: deliver 400 units while storing 300 units. In week 3, deliver the 300 units that were stored. In week 4, produce and deliver 500 units.

结题思路

思路1:

  1. 第一周的牛奶自给自足。
  2. 第二周的牛奶判断上一次(上一次生产的周不一定为上周)的生产费用+上一次生产的周到此周所有单位牛奶所花的存储费用是否大于此周的生产费用,若大于,则此周生产;若小于则上一次生产时多生产此周交付量的牛奶。
  3. 以后的周数以此类推。
  4. 输出最后的sum。

源代码

#include <iostream>
#include <cmath>
#include <stdlib.h>
using namespace std;
long long c[10005], y[10005];
int main()
{
	int n, s;
	long long sum = 0;
	cin >> n >> s;
	for(int i = 0; i < n; i++)
	{
		cin >> c[i] >> y[i];
	} 
	sum = c[0] * y[0];
	int flag = 0;
	for(int i = 1; i < n; i++)
	{
		if(c[i] >= c[flag] + (i-flag) * s)
		{
			sum += (y[i] * c[flag] + (i-flag) * s * y[i]);
		}
		else 
		{
			sum += y[i] * c[i];
			flag = i;
		}
	}
	cout << sum << endl;
	return 0;
} 

题型分析

  1. (此解法是用贪心算法做的,但感觉其不一定是最优的。要用动态规划做才是最优的(设置两个循环,并判断最优条件)。)
  2. flag的设置,标志为上一次生产的周数。
  3. 贪心算法:判断当选的选择与之前最优的选择谁更优,就选用哪种方法。

下次遇到此类题我要注意的地方

1.动态规划贪心算法题,要特别注意改变某个位置最大值的条件(要不要在上次生产时,多生产一些)
  1. 英文题目;写完中文翻译之后,再去看英文,仔细理解题意。翻译熟能生巧(看到英文界面不要怕,第一次做此种类型时,把它从头到尾翻译一遍,就知道到底是什么水平了)。
  2. 贪心算法要善于找到最优的比较条件,如花费的钱最后(需追溯到数组的上几位数)。

此类题模板代码


7. 无线网络 = 并查集问题 + 找父亲函数 + 将已修复的电脑表带连接到同一个父亲上(还需要做并查集的题)

题目描述

Description

An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wireless network with the lap computers, but an unexpected aftershock attacked, all computers in the network were all broken. The computers are repaired one by one, and the network gradually began to work again. Because of the hardware restricts, each computer can only directly communicate with the computers that are not farther than d meters from it. But every computer can be regarded as the intermediary of the communication between two other computers, that is to say computer A and computer B can communicate if computer A and computer B can communicate directly or there is a computer C that can communicate with both A and B.

In the process of repairing the network, workers can take two kinds of operations at every moment, repairing a computer, or testing if two computers can communicate. Your job is to answer all the testing operations.

Input

The first line contains two integers N and d (1 <= N <= 1001, 0 <= d <= 20000). Here N is the number of computers, which are numbered from 1 to N, and D is the maximum distance two computers can communicate directly. In the next N lines, each contains two integers xi, yi (0 <= xi, yi <= 10000), which is the coordinate of N computers. From the (N+1)-th line to the end of input, there are operations, which are carried out one by one. Each line contains an operation in one of following two formats:

  1. “O p” (1 <= p <= N), which means repairing computer p.
  2. “S p q” (1 <= p, q <= N), which means testing whether computer p and q can communicate.

The input will not exceed 300000 lines.

Output

For each Testing operation, print “SUCCESS” if the two computers can communicate, or “FAIL” if not.

Sample Input

4 1
0 1
0 2
0 3
0 4
O 1
O 2
O 4
S 1 4
O 3
S 1 4

Sample Output

FAIL
SUCCESS

结题思路

思路1:并查集问题(设置flag和容器来解决)

  1. 初始化每台电脑的位置,每台电脑未修复的flag等于其序列i。
  2. 定义一个vector类型的容器,将每个电脑能连接的其他的电脑的序列i push_back进其容器。
  3. 当输入’O’时,将此电脑修复,并将其表带相连到每一个已修复的电脑祖宗中(father[rooty] = rootx),即father[rooty]的坐标值指向rootx
  4. 当输入’S’时,判断其最开始的祖宗是不是同一个,若是则输出success,若不是则输出fail。

源代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>

#include <cmath>
using namespace std ; 

#define maxn 1010 
bool flag[maxn] ;
int father[maxn] ; 

vector<int> vv[maxn] ; 
int n , d ; 

struct node {
    int x , y ;
} post[maxn] ; 

double fun(int x){
    return x*x*1.0 ; 
}

void init(){
    memset(vv , 0 , sizeof(vv)) ; 

    for(int i=1 ; i<=n ; i++){
        father[i] = i ; //初始化 每台电脑为一个集合
        flag[i] = false ;// 所有电脑 初始状态为损坏 

        for(int j =1 ; j<=n ; j++){
            if(i==j){
                continue ; 
            }
            //为 i 电脑 链接 所有 可连接距离内的电脑
            double dis = sqrt(fun(post[i].x-post[j].x)+fun(post[i].y-post[j].y)) ; 
            if(dis<=d){
                vv[i].push_back(j) ; 
            }
        }
    }
    for(int i = 0 ; i < n; i++)
	{
		cout << "vv" << i << ":";
		for(int j = 0; j < vv[i].size(); j++)
		{
			cout << vv[i][j];
		}
		cout << endl;
	}
}
//查询 x 所在集合的代表元素的编号
//同时 理顺集合内元素的关系(为刚合并的两个集合找出统一的代表)
int find(int x){
    if(x!=father[x])
        father[x] = find(father[x]) ; 
    return father[x] ; 
}

void Union_set(int x , int y){
    int rootx = find(x) ; 
    int rooty = find(y) ; 

    if(rootx != rooty){
        father[rooty] = rootx ; // 将 第二个集合的带表元素 连接到第一个集合 
    }
}



int main(){

    while(~scanf("%d %d" , &n , &d)){
        for(int i=1 ; i<=n ; i++){
            scanf("%d%d" , &post[i].x , &post[i].y) ; 
        }
		
        init() ; 
        char str[3] ; 
        int x , y ; 

        while(~scanf(" %s" , str)){
            //scanf(" %s" , str) ; 

            if(str[0] == 'O'){
                scanf("%d" , &x) ; 
                flag[x] = true ; 
                for(int j=0 ; j<vv[x].size() ; j++){
                    int k = vv[x][j] ; 
                    if(flag[k]){//编号为 k 的电脑已经被修复 
                        Union_set(x , k ) ; 
                    }
                }
            }else if(str[0] =='S'){
                scanf("%d %d" , &x , &y) ; 
                int rootx = find(x) ; 
                int rooty = find(y) ; 
                if(rootx == rooty){
                    printf("SUCCESS\n") ; 
                } else {
                    printf("FAIL\n") ; 
                }
            }
        }
        
    }
    return 0 ; 
}
#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn  1005
bool flag[maxn];
int father[maxn];
struct node{
	int x, y;
}pos[maxn];
vector<int> vv[maxn]; // 容器数组 
int n, d; // $1. 全局变量的好处就是不用在函数中传来传去了。 
void initSet()
{
	memset(vv, 0, sizeof(vv));
	for(int i = 0; i < n ; i++)
	{
		cin >> pos[i].x >> pos[i].y;
	}
	for(int i = 0; i < n; i++)
	{
		flag[i] = 0; 
		father[i] = i; // 每个点为自己的祖先 
		for(int j = 0; j < i; j++)
		{
			if(i == j)
			{
				continue;
			}
			if(pow((pos[i].x-pos[j].x), 2) + pow((pos[i].y-pos[j].y), 2) <= pow(d, 2))
			{
				vv[i].push_back(j);
			}
		}
	}
}

int getFather(int x)
{
	if(x != father[x])
	{
		father[x] = getFather(father[x]);
	}
	return father[x];
}

void becomeAFamily(int x, int y)
{
	int rootx = getFather(x);
	int rooty = getFather(y);
	if(rootx != rooty)
	{
		father[rooty] = rootx;
	}
}

int main()
{
	cin >> n >> d;
	initSet();
	char str[3];
	int x, y;
	while(~scanf(" %s", str))
	{
		if(str[0] == 'O')
		{
			scanf("%d" , &x) ; 
			flag[x] = 1;
			for(int j = 0; j < vv[x].size(); j++)
			{
				int k = vv[x][j];
				if(flag[k])
				{
					becomeAFamily(x, k);
				}
			}
		}
		if(str[0] == 'S')
		{
			cin >> x >> y;
			int rootx = getFather(x);
			int rooty = getFather(y);
			if(rootx == rooty)
			{
				cout << "SUCCESS" << endl;
			}
			else
			{
				cout << "FAIL" << endl;
			}
		}
	}
	return 0;
} 

题型分析

  1. 并查集问题,使用成为"成为家庭"和"找父亲"的方法解决。

下次遇到此类题我要注意的地方

1.熟悉"成为家庭"和"找父亲"函数。
  1. 测试它的样本是怎么从出发到结束的。
  2. 特别注意所有的电脑都坏了,需要一个一个来修复;并且,当一个电脑修复后,其肯定能和其他。
  3. memset初始化数组、容器等。
  4. 代码1,是不是应该还要考虑当x和k的祖宗不能成为一家人时,就不需要进行集合的连接呢?这里还需要考虑一下。
  5. 代码2有一点问题,没找出来。

此类题模板代码

  1. 构造可达集合模板代码
for(int i=1 ; i<=n ; i++){
        father[i] = i ; //初始化 每台电脑为一个集合
        flag[i] = false ;// 所有电脑 初始状态为损坏 

        for(int j =1 ; j<=n ; j++){
            if(i==j){
                continue ; 
            }
            //为 i 电脑 链接 所有 可连接距离内的电脑
            double dis = sqrt(fun(post[i].x-post[j].x)+fun(post[i].y-post[j].y)) ; 
            if(dis<=d){
                vv[i].push_back(j) ; 
            }
        }
    }
  1. 找父亲和并查集的模板代码
//查询 x 所在集合的代表元素的编号
//同时 理顺集合内元素的关系(为刚合并的两个集合找出统一的代表)
int find(int x){
    if(x!=father[x])
        father[x] = find(father[x]) ; 
    return father[x] ; 
}

void Union_set(int x , int y){
    int rootx = find(x) ; 
    int rooty = find(y) ; 

    if(rootx != rooty){
        father[rooty] = rootx ; // 将 第二个集合的带表元素 连接到第一个集合 
    }
}

8. Arctic Network = 距离的问题划为排序题 + 计算每个基站与前i-1个基站的距离存到动态数组中 + (int *)malloc(sizeof(int) * n)

题目描述

http://bailian.openjudge.cn/practice/2349/

结题思路

思路1:

  1. 每输入一个基站坐标时,计算其与前面i-1个基站的距离,存入distance数组中。
  2. 给distance数组排序,输入v-sitelliteC的值。

源代码

#include <iostream>
#include <cmath>
#include <iomanip>
#include <stdlib.h>
#include <algorithm>
using namespace std;
int coordinate[501][2] = {0}; 
int main()
{
	int n;
	int *a;
	int sitelliteC, outpostC;
	double *distance;
	int v = 0;
	cin >> n;
	for(int i = 0; i < n; i++)
	{
		a = (int *)calloc(n, sizeof(int));
		//a = (int *)malloc(sizeof(int)*n);
		cin >> sitelliteC >> outpostC;
		distance = (double *)calloc(outpostC*(outpostC-1)/2, sizeof(double));
		for(int j = 0; j < outpostC; j++)
		{
			cin >> coordinate[j][0] >> coordinate[j][1];
			for(int p = 0; p < j; p++)
			{
				distance[v] = sqrt(pow(coordinate[j][0]-coordinate[p][0], 2) + pow(coordinate[j][1]-coordinate[p][1], 2));
				v++;
			}
			sort(distance, distance+v, greater<double>());
		}
//		for(int i = 0; i < v; i++)
//			{
//				cout << "distance[" << i << "]:" << distance[i] << endl;
//			}
//		cout << v << endl;
//		cout << sitelliteC << endl;
		cout << setiosflags(ios::fixed) << setprecision(2) << distance[v-sitelliteC] << endl;
	}
	return 0;
} 

题型分析

  1. c++格式输出
#include <iomanip>
cout << setiosflags(ios::fixed) << setprecision(2) <<

下次遇到此类题我要注意的地方

1.setiosflags(ios::fixed) << setprecision(2)。2.sort()函数。

此类题模板代码

  1. c++格式输出
#include <iomanip>
cout << setiosflags(ios::fixed) << setprecision(2) <<
  1. sort函数模板
#include <algorithm>
sort(a, a+n, greater<>())

9. 数与字符串 = 从左到右对照每一个数排列,每一个数都尽量取最大的(9) + 整型与字符串的转换(to_string()整型转字符串 + itoa(num, a, 10)整型转char [])

题目描述

http://bailian.openjudge.cn/xly2019/A/

结题思路

思路1:

  1. 输入一个整数。
  2. 将整数转化为字符串,记位数为len,并判断此字符串的位数,若len为1,则直接输出此字符串。若大于1,则进入下一步。
  3. 判断此字符串的最高位的情况,若最高位为9,则是直接输出输出此字符串;若最高位不为9,则输出len-1位9。

源代码

#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
using namespace std;
int MAX = 10000; 
int main()
{
	string a[MAX];
	string res[MAX];
	int num;
	cin >> num;
	int i = 0;
	while(num != 0)
	{
		if(num < 10)
		{
			res[i] = to_string(num);
		}
		else
		{
			a[i] = to_string(num);
			if(res[i][0] == '9')
			{
				res[i] = a[i];
			}
			else
			{
				int len = a[i].length();
				int tmp = pow(10, len-1)-1;
				res[i] = to_string(tmp);
			}
			cin >> num;
			i++;
		}
	}
	for(int j = 0; j < i; j++)
	{
		cout << res[j] << endl;
	}
	return 0;
} 

题型分析

  1. 字符串和整型转换题、夹杂着字典序。
  2. 字典序:从左到右对照每一个数排列,每一个数都尽量取最大的(9)。

下次遇到此类题我要注意的地方

  1. 对于string类型
    使用to_string(int value)将整型转为string类型。
  2. 对于char a[]
    使用itoa(int value, char * a, radix)将整型转换为char a[]。
  3. memset()的头文件是cstring。
    ||易|中|难|
    |-|-|-|-|
    |紧||1. 字符串的转换。2. 字典序。||
    |般||||
    |必||||

此类题模板代码

  1. 整型转string类型
res[i] = to_string(num);
  1. 整型转char a[]型,char a[]转string。
int num;
cin >> num;
char a[10];
// 整型转char a[]型,itoa,a为char a[]。
itoa(num, a, 10);
// char a[]转string,直接赋值
string b = a;
cout << b;

题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


题目描述

结题思路

源代码

题型分析

下次遇到此类题我要注意的地方

此类题模板代码


<–>

猜你喜欢

转载自blog.csdn.net/qq_40092110/article/details/104884783