BuaaCoding 071-99 Problems and Solutions

PART I 简单题

071 Ryan’s Confusing Words我没看懂这题啥意思。。。。这个OJ最大的问题就是喜欢自说自话,根本也搜不到。。。辣鸡
072 晴天小猪的羊肉串 (虽然水 但是那个提示会告诉你反向的问题,这是一个考虑问题防止不全面的地方)
073 随便玩玩的字符串
074 临近的虚实
075 恶魔的证明
076 jhljx学素数
077 jhljx的强迫症 看上去很麻烦其实就是互质判断 啊我居然自己解决了一道数学题
081 第二小数
083 因子查找
086 几何找规律
087 Magry遇上了初雪 分数约减
088 A plus B
090 晴天小猪的绕口令 字符串
091 n的最小cp 反向构造表
093 伤心的晴天小猪
098 第三小整数
099 bfs 但是蜜汁出错。。

PART II 算法题

079 microhhh的雷达(字符串)

题目

microhhh一路来到了jhljx的老巢汉中市,打开AN/APG-77雷达进行大规模搜索,雷达有A,B两种信号反馈,A类信号为一串数字(长度为8的整数倍),为系统提示信息,具体为一个八位二进制数来表示一个ASCII码,(例如01000001表示字符’A’). B类信号为一个字符串,为雷达回波,用来指示侦查结果,为了简化问题,我们假设当前雷达只能识别两个汉中人,jhljx,xihang,如果雷达回波中含有这两个关键人名,则接下来的两个十六进制数为目标的坐标,十六进制数以#结尾,可能有多余的前导0,例如 &&jhljx$$0x20#,0x25#!!名字和坐标之间可能会有若干个其他字符隔开,但保证输入合法,换言之,有可能人名后面出现两个十六进制数,也可能没有十六进制数,但不存在只有一个十六进制数的情况,也不会出现三个及以上的十六进制数,不符合0x–#格式的数字视为乱码,非法的十六进制数也视为乱码。一段回波中每个人最多出现一次,两个人同时出现则输出时按照先后顺序以空格隔开,不要输出多余的前导0,如果雷达回波仅为乱码,则输出”nothing found!“,具体格式见样例
输入
多组数据,每组输入两行 第一行为一个字符A或B,代表雷达信号的种类 第二行为一个字符串,代表数据的具体内容
输出
对于A类信号,输出对应的提示内容
对于B类信号,输出对应的目标和坐标

程序可以处理任意一种信号即可获得一半的分数,因为B类情况比较复杂,但是非常有趣,大家可以上机后试试,非常的有趣,B类信号的样例即为测试数据,但是直接输出样例是过不了的,本题一半的分数即为AC

分析

这个题因为B类的测试点和样例一样,所以为了避免打表,它要求强制在线,所以如果想通过存在数组里再分析的方法是不可能的,它可以一个字符一个字符吐,然后TLE。因为这个事贡献了无数次WA。
所以我已开始以为要用有限状态机,但是十分复杂。看到有大神提出采用Hash方法,不禁感叹其精妙所在。
A类信号很简单,关键看B类。
核心思想1:如何判断谁出现了?
其基本思想是,每三位数表示一个字符,那么jhljx和xihang两个名字分别可以用15位和18位数表示。在出现他们的名字之前,字符都没有保留的意义。出现名字之后,我们只要记录下来接下来出现的坐标应该算谁的就可以了。所以我只需要维护最后5~6个字符所对应的数字,也就是代码中的wait1和wait2,根据wait1和wait2能否被两个名字对应的编码整除,可以知道前面出现了谁的名字,所以我接下来会根据名字设置firstPersonCord/secondPersonCord。
如何知道坐标是否合法,以及具体值是多少
为了知道数字的合法开头,需要记录前一个字符,和当前字符结合起来判断是不是合法开头,是的话,把getnum set 1,以后遇到数字就要记录,遇到#好就要把数字存到坐标里,遇到非法字符就要getnum set 0。
如何输出?
设置一个flag,一开始时true,只要有输出,就会set 0。所以如果读完了字符还是true,说明没有输出,则输出nothing found!,反之,如果有输出,我会把flag set0,这样的逻辑其实第一次输出之后一定会紧跟一个空格,这里充分利用了一个特性,就是一般评测机不检查每一行最后的空格,所以即使没有第二个人的坐标输出,后面多余的空格不影响正确性。

解答

#include <cstdio>
#include <iostream>
const long long n1 = 106104108106120;
const long long n2 = 120105104097110103;
const long long filter1 = 1000000000000000,filter2 = 1000000000000000000;
int main(int argc, char* argv[]) {
	char ch;
	while(std::cin>>ch){
		getchar();
		if(ch=='A'){
			int base = 0;
			int sum = 0;
			while((ch = getchar()) != '\n') {
				sum = sum * 2 + (ch - '0');
				base++;
				if (base == 8) {
					base = 0;
					printf("%c",sum);
					sum = 0;
				}
			}
		}else if (ch == 'B') {
			long long wait1 = 0, wait2 = 0;
			long long tem = 0;
			long long x, y;
			char old = '1';
			int firstPersonCord = 0; 
			int secondPersonCord = 0;
			int flag = 1;
			int getnum = 0;  //表示现在是否是一个读取数字的状态
			while((ch=getchar()) != '\n') {
				//每个字符用三位数表示一个字符
				//wait2表示的后6位字符,wait1表示后5位字符,也就是用来判断是否有人
				wait2 = (wait1 * 1000 + ch) % filter2;
				wait1 = wait2 % filter1;
				//最后5位数是jhljx 开始等待判断1的坐标firstPersonCord set 1
				if(wait1 == n1) {
					firstPersonCord = 1;
					secondPersonCord = 0;
					x = -1;
					y = -1;
				}
				//最后6位数是xihang 开始等待判断2的坐标secondPersonCord set 1
				if(wait2 == n2) {
					firstPersonCord = 0;
					secondPersonCord = 1;
					x = -1;
					y = -1;
				}
				//当前的数字有意义
				if(getnum && (firstPersonCord || secondPersonCord)) {
					//遇到# 数字结束 按照xy的顺序放到坐标上
					if (ch == '#') {
						if (x == -1) {
							x = tem;
						} else {
							y = tem;
						}
						getnum = 0;
					}
					//没有遇到#判断数字时候合法 不合法getnum set0
					if('0'<=ch&&ch<='9')tem=tem*16+ch-'0';
					else if('a'<=ch&&ch<='f')tem=tem*16+ch-'a'+10;
					else tem=0,getnum=0;
				}
				//坐标有意义 输出坐标
				if(x != -1 && y != -1 && (firstPersonCord||secondPersonCord)) {
					if(firstPersonCord) {
						printf("jhljx(%lld,%lld)",x,y);
					} else {
						printf("xihang(%lld,%lld)",x,y);
					}
					if(flag) {
						printf(" ");
					}
					flag = 0;
					x = -1; 
					y = -1;
				}
				//上一次和这一次构成0x 下面开始计数 getnum set1
				if(old == '0' && ch == 'x') {
					getnum = 1;
				}
				old = ch;
			}
			if(flag) {
				printf("nothing found!");
			}
		}
		printf("\n");
	}
}

092 jhljx分解质因数

题目

jhljx最近在学小学数学,老师教他分解质因数。也就是说给你一个数n,让你把它分解成若干个质数的乘积的形式。
输入
输入多组数据。
每组数据一行,为一个数n。(1<=n<=10^6)
输出
每组数据输出一行。为一个表达式。表达式中的数字必须按从小到大的顺序排列。

分析

经典问题,比较简单的写法就是对每个小于n的数,能整除就一直除到不能除为止。这个方法看上去根本没有判断因子是不是质数,这是因为从小到大,能除的都除掉了,合数没有机会再整除。
时间复杂度O(n1/4)

解答

#include <stdio.h>
int main(int argc, char *argv[]) {
	int n;
	while(scanf("%d", &n) != EOF) {
		for (int i = 2; i <= n; i++) {
			while (n != i) {
				if (n % i == 0) {
					printf("%d*", i);
					n /= i;
				} else {
					break;
				}
			}
		}
		printf("%d\n", n);
	}
}

094 双层汉诺塔

题目

想必,前面的汉诺塔对于大家来说,just a piece of cake!
我们来玩一个更高级的汉诺塔游戏——双层汉诺塔。双层汉诺塔游戏中,相同大小的盘都有颜色不同的两片,按照次序放在最左边的金刚柱上。 游戏的目的是把不同颜色的盘分别按照上小下大的顺序放在中间和右边的两个柱子上。在移动过程中,在移动过程中, 依旧是遵守大盘必须在小盘之下,而颜色顺序无限制。
下面给出的四个盘子的例子,当然,这个图片是不完整的,还需要把左边粉红色的盘子移到最右边的柱子上,把黄色的盘子移动到中间的柱子上。
输入
第一行输入一个整数t(t < 10),表示测试的组数。
接下来的t行,每行输入一个正偶数n(0 <n<=20),表示盘子的个数。
输出
对于每组测试数据,首先输出一行Case #X:,表示第X组测试数据。 接下来,每行都输出移动盘子的步骤,对于每次移动,以"Move from X to Y"表示,表示需要将X柱子上最顶层的盘子移动到Y柱子上,其中X,Y是A,B,C中的一个,当然,X和Y不可能相同

分析

两个状态的递归,一种是把所有的盘子都移动到一个柱子上,另一种是移动到两个柱子上。

解答

#include <stdio.h>
void hanoi(int n, int oneOrTwo, char src, char mid, char dst) {
	if (n == 2) {
		if (oneOrTwo == 2) {
			printf("Move from %c to %c\n", src, mid);
			printf("Move from %c to %c\n", src, dst);
		} else {
			printf("Move from %c to %c\n", src, dst);
			printf("Move from %c to %c\n", src, dst);
		}
		return;
	}
	if (oneOrTwo == 2) {
		hanoi(n-2, 1, src, mid, dst);
		printf("Move from %c to %c\n", src, mid);
		printf("Move from %c to %c\n", src, mid);
		hanoi(n-2, 1, dst, mid, src);
		printf("Move from %c to %c\n", mid, dst);
		hanoi(n-2, 2, src, mid, dst);
	} else {
		hanoi(n-2, 1, src, dst, mid);
		printf("Move from %c to %c\n", src, dst);
		printf("Move from %c to %c\n", src, dst);
		hanoi(n-2, 1, mid, src, dst);
	}
}
int main(int argc, char *argv[]) {
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++) {
		int n;
		scanf("%d", &n);
		printf("Case #%d:\n", i);
		hanoi(n, 2, 'A', 'B', 'C');
	}
}

096 暗号

题目

统计两个字符串的LCS长度和可能的答案个数。

分析

LCS很简单,关键是答案个数有意思。

解答

这段代码没有在OJ上AC,这道题我做的时候都没有人AC,但是这道题也是Code[VS]1862题,交上去是AC的。。。。恕我才疏学浅,实在不能领悟我航OJ的精妙。。。。应该不是因为没用滚动数组,因为我的滚动数组版本同样过Code [VS]1862 不过两道题好像也不完全一样,北航OJ要求两个位置不一样的相同字幕不等价

#include <stdio.h>
#include <string.h>
#define M 100000000
char s[5005];
char t[5005];
int dp[5005][5005];
int num[5005][5005];
int max(int a, int b) {
	return a > b?a:b;
}
void cal(int lens, int lent) {
	dp[0][0] = 0;
	for (int i = 0; i <= lens; i++) {
		dp[i][0] = 0;
		num[i][0] = 1;
	}
	for (int j = 0; j <= lent; j++) {
		dp[0][j] = 0;
		num[0][j] = 1;
	}
	for (int i = 1; i <= lens; i++) {
		for (int j = 1; j <= lent; j++) {
			if (s[i - 1] == t[j - 1]) {
				dp[i][j] = dp[i-1][j-1] + 1;
				num[i][j] = num[i - 1][j - 1];
				if (dp[i][j] == dp[i][j - 1]) {
					num[i][j] = (num[i][j] + num[i][j-1]) % M;
				}
				if (dp[i][j] == dp[i - 1][j]) {
					num[i][j] = (num[i][j] + num[i - 1][j]) % M;
				}
			} else {
				dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
				if (dp[i][j] == dp[i][j - 1]) {
					num[i][j] = (num[i][j] + num[i][j-1]) % M;
				}
				if (dp[i][j] == dp[i - 1][j]) {
					num[i][j] = (num[i][j] + num[i - 1][j]) % M;
				}
				if (dp[i][j] == dp[i - 1][j - 1]) {
					num[i][j] -= num[i - 1][j - 1];
					num[i][j] = (num[i][j] + M) % M;
				}
			}
		}
	}
}
int main(int argc, char *argv[]) {
	while(scanf("%s", s) != EOF) {
		scanf("%s", t);
		int lens = strlen(s) - 1;
		int lent = strlen(t) - 1;
		cal(lens, lent);
		printf("%d\n%d\n", dp[lens][lent], num[lens][lent]);
	}	
}

097 Tiling_easy version

题目

有一个大小是 2 x n 的网格,现在需要用2种规格的骨牌铺满,骨牌规格分别是 2 x 1 和 2 x 2,请计算一共有多少种铺设的方法。

分析

f[i] = f[i - 1] + f[i - 2] + f[i-2]
f[i - 1]:最后一列竖着放一个 12
f[i - 2]:最后两列横着放两个1
2 注意不需要考虑竖着放,已经包含在第一种里了
f[i - 2]:最后两列放一个2*2

解答

#include <stdio.h>
int main(int argc, char *argv[]) {
	int memo[35];
	memo[1] = 1;
	memo[2] = 3;
	for (int i = 3; i <= 30; i++) {
		memo[i] = 2 * memo[i - 2] + memo[i - 1];
	}
	int N;
	while(scanf("%d", &N) != EOF) {
		printf("%d\n", memo[N]);
	}
	return 0;
}

PART III 数学题

078 无法禁止的游戏

题目

KamuiKirito和jhljx玩游戏。
一个由n条横线m条竖线构成的网格。
例如一个3*3的网格
每次一方选择一个交点,将该点所在的横线和竖线删掉然后又另一方进行。 进行时如果网格不存在交点则失败。
jhljx大神先来。
输入
多组测试数据
每组测试数据一行,两个整数,为n,m。(1<=n,m<=100)
输出
对于每组输入数据,输出一行,为获胜方。

分析

第一道自己做出来的博弈论题目,感动。。。
首先发现这根本不是一个几何问题,策略和选择哪个点无关,反正最后网格都会变小,所以,只需要像威佐夫博弈那样用数对表示局势,别的自己推一下,还是很明显的。

解答

#include <stdio.h>
int main(int argc, char *argv[]) {
	int n, m;
	while(scanf("%d%d", &n, &m) != EOF) {
		if (n > m) {
			int t = n;
			n = m;
			m = t;
		}
		if (n % 2) {
			printf("jhljx\n");
		} else {
			printf("KamuiKirito\n");
		}
	}
	return 0;
}

085 三角形

题目

给n个三角形,最多能把平面划分成几个部分。

分析

设n个三角形最多将平面分成pn个部分。
n=1时,p1=2
n=2时,第二个三角形的每一条边与第一个三角形最多有2个交点,三条边与第一个三角形最多有2×3=6(个)交点.这6个交点将第二个三角形的周边分成了6段,这6段中的每一段都将原来的每一个部分分成2个部分,从而平面也增加了6个部分,即-p2=2+2×3.
n=3时,第三个三角形与前面两个三角形最多有4×3=12(个)交点,从而平面也增加了12个部分,即:p3=2+2×3+4×3.
……
一般地,第n个三角形与前面(n-1)个三角形最多有2(n-1)×3个交点,从而平面也增加2(n-1)×3个部分,故
pn=2+2×3+4×3+…+2(n-1)×3
=2+〔2+4+…+2(n-1)〕×3
=2+3n(n-1)=3n^2-3n+2.

解答

#include <stdio.h>
int main(int argc, char *argv[]) {
	int n;
	while(scanf("%d", &n) != EOF) {
		printf("%d\n", 2 + 3*n*(n-1));
	}
}

095 异面直线距离

题目

如题

分析

按照公式设两条直线的方向向量为:s1,s2,则两条直线的距离为 ∣ s 1 × s 2 ⋅ M N ∣ ∣ s 1 × s 2 ∣ \frac{|s_1\times s_2\cdot MN|}{|s_1\times s_2|} s1×s2s1×s2MN,其中MN是从两条直线上各任取一点得到的向量。
几何意义,由s1,s2和MN构成平行六面体的体积,除以底面s1, s2构成的底面积,得到的高就是两个直线的距离。

解答

#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]) {
	int T;
	scanf("%d", &T);
	while(T--) {
		long long x, y, z;
		scanf("%lld%lld%lld", &x, &y, &z);
		printf("%.5lf\n", x*y*z/sqrt(x*x*y*y+y*y*z*z+x*x*z*z));
	}
	return 0;	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值