2021.07.16 总结

2021.07.16 总结

​ 今天状态不怎么好,几道那么容易的题就只有140分,毕竟也就打了前两道

T1 花生采摘

题目描述

鲁宾逊先生有一只宠物猴,名叫多多。这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!”。 鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1)。

有经验的多多一眼就能看出,每棵花生植株下的花生有多少。为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”

在这里插入图片描述
我们假定多多在每个单位时间内,可以做下列四件事情中的一件:

  1. 从路边跳到最靠近路边(即第一行)的某棵花生植株;
  2. 从一棵植株跳到前后左右与之相邻的另一棵植株;
  3. 采摘一棵植株下的花生;
  4. 从最靠近路边(即第一行)的某棵花生植株跳回路边。

现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。 例如在图2所示的花生田里,只有位于(2, 5), (3, 7), (4, 2), (5, 4)的植株下长有花生,个数分别为13, 7, 15, 9。沿着图示的路线,多多在21个单位时间内,最多可以采到37个花生。

【样例输入1】 
6 7 21
0 0 0 0 0 0 0
0 0 0 0 13 0 0
0 0 0 0 0 0 7
0 15 0 0 0 0 0
0 0 0 9 0 0 0
0 0 0 0 0 0 0
【样例输出1】   
37 

个人题解

我的理解如下:

​ 6 7 21
​ 0 1 2 3 4 5 6 7
​ 0 / / / / / / / / / / / /
​ 1 / 0 0 0 0 0 0 0
​ 2 / 0 0 0 0 2 0 0
​ 3 / 0 0 0 0 0 0 4
​ 4 / 0 1 0 0 0 0 0
​ 5 / 0 0 0 3 0 0 0
​ 6 / 0 0 0 0 0 0 0

输入时用数组记录 x[i](行)y[i](列)z[i](花生数)

然后用qsort一下花生数(从大到小)

如何求花生之间的距离和所需的时间?

用曼哈顿公式(设 i 点与 i-1 ):

​路程=|x[i]-x[i-1]|+|y[i]-y[i-1]|

时间=|x[i]-x[i-1]|+|y[i]-y[i-1]|*走一格所需的单位时间

PS:要初始化起点 x[0]=0,y[0]=y[1](垂线段最短),z[0]=0;

timesum加上走路的时间,再加上采摘的时间,再加上回到路上的时间

判断一下会不会超时

若会,输出当前ans,return 0

若不会,timesum减去回到路上的时间,ans加上z[i]

结果没有看清楚,题目上写,没有重复的,然后qsort就多了个判断,然后卡死了

某些同学的题解:

把多多咔嚓掉,呸,送去好地方。。

T2 FBI树 和某国喊“OPEN THE DOOR”的那帮人无关

题目描述

我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

FBI树是一种二叉树[1],它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:

  1. T的根结点为R,其类型与串S的类型相同;

  2. 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。

现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历[2]序列。

[1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。

[2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。

样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF
提示

在这里插入图片描述

个人题解

​ 看到提示,本蒻立刻想到了完全二叉树

​ 二叉树嘛,一维数组给他存着,父节点就 i / 2 ,左子节点就 2 * i ,右子节点就 2 * i + 1

但是他居然要我输出这棵树的后序遍历!这下就有一丢丢难办了。。

​ 然后?然后脑回路十分清奇,也懒得重打的蒟蒻的我用了递归输出

​ 至于规律嘛——要是两个儿子中有一个是“F”,或互不相同,这个节点就是“F”,否则两个儿子是什么,这个节点就是什么

特殊给个code主要是也不好解释递归那段

#include<cstdio>
#include<cstring>
using namespace std;
int n,len=1,p=0;
char ch[20001],a[1001];
void dg(int gan){//gan是随便定义的
	printf("%c",ch[gan]);
	gan--;
	if(gan%2==0){//判断是否输出了右儿子(后序遍历需要)
		dg(gan/2);
	}
	return ;
}
int main(){
	scanf("%d",&n);
	scanf("%s",1+a);
	while(n!=0){
		len=len*2;
		n--;
	}
	for(int i=1;i<=len;i++){//树中最底层的
		if(a[i]=='1'){
			ch[len+i-1]='I';
		}else{
			ch[len+i-1]='B';
		}
	}
	for(int i=len-1;i>=1;i--){//树除底层外的上面几层
		if(ch[2 * i]=='F' or ch[2 * i+1]=='F' or (ch[2 * i]=='I' and ch[2 * i+1]=='B') or (ch[2 * i]=='B' and ch[2 * i+1]=='I')){//因为底层已经求过了,所以可以直接求前几层
			ch[i]='F';
		}else if(ch[2 * i]=='B'){
			ch[i]='B';
		}else if(ch[2 * i]=='I'){
			ch[i]='I';
		}
	}
	for(int i=len;i<=len*2-1;i++){
		dg(i);
	}
	return 0;
}

T3 火星人

题目描述

人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。 火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。 一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字: 三进制数 123 132 213 231 312 321 代表的数字 1 2 3 4 5 6 现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。

输入

输入文件martian.in包括三行,第一行有一个正整数N,表示火星人手指的数目(1 <= N <= 10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M <= 100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。

输出

输出文件martian.out只有一行,这一行含有N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。

样例输入
5
3
1 2 3 4 5
样例输出
1 2 4 5 3

个人理解

阿巴阿巴~~~

实际上就是全排列

好像还有神马康托展开:

康托展开是一个全排列到一个自然数双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。(百度百科

公式:
X = a n ( n − 1 ) ! + a n − 1 ( n − 2 ) ! + . . . + a 1 ∗ 0 ! X=a_n(n-1)!+a_{n-1}(n-2)!+...+a_1*0! X=an(n1)!+an1(n2)!+...+a10!
而这道题两个都要用到

关于逆康托展开,举个栗子:

数列 34152

用 61 / 4! = 2余13,说明比首位小的数有2个,所以首位为3。

用 13 / 3! = 2余1,说明在第二位之后小于第二位的数有2个,所以第二位为4。

用 1 / 2! = 0余1,说明在第三位之后没有小于第三位的数,所以第三位为1。

用 1 / 1! = 1余0,说明在第四位之后小于第四位的数有1个,所以第四位为5。

最后一位就是剩下的数2。

好了解释完以后,说一说步骤

首先用康托展开公式求出样例中的数列所代表的数

然后再用逆康托展开公式求出新数列

最后输出

T4 麦森数

题目描述

在这里插入图片描述

个人理解

用快速幂和高精度乘法

我一开始还担心 p<3100000 ,后来,我总算学会了,如何去爱…,呸,一个求位数的不知名公式:
( p / l o g 2 ( 10 ) ) + 1 (p/log_2(10))+1 (p/log2(10))+1
(知道叫什么的大奆在底下评论一下,知道后,我会尽早修改,谢谢!)

因此我们只要开一个 600 的数组即可(<=500)

当然,也可以用压位解决这道题

总结

感觉不咋滴,其实好好想,仔细看题,还是能做出来的

下次要稳住才行!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值