工作室课题4
练习
高僧斗法
Description
古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。
节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图1所示)
两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。
两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。
对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。
Input
输入数据为一行用空格分开的N个整数,表示小和尚的位置。台阶序号从1算起,所以最后一个小和尚的位置即是台阶的总数。(N<100, 台阶总数<1000)
Output
输出为一行用空格分开的两个整数: A B, 表示把A位置的小和尚移动到B位置。若有多个解,输出A值较小的解,若无解则输出-1。
Sample Input 1
1 5 9
Sample Output 1
1 4
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
#Nim博弈问题,把两个和尚看成一组,然后进行异或,
#本例中为:3、3。
# 取异或,异或为非0,先手赢,异或为0,先手输。
#所以我们只需要判断取异或为0即可
h = list(map(int, input().split()))
hlen = len(h)
d = [0 for _ in range(hlen - 1)]
for i in range(1, hlen):
d[i - 1] = h[i] - h[i - 1] - 1
sum = 0
for i in range(0, hlen - 1, 2):
sum ^= d[i]
# print(sum)
if sum == 0:
print(-1)
else:
for i in range(hlen - 1):
step = 1
while True:
# print(h)
if h[i] + step >= h[i + 1]:
break
d[i] -= step
if (i != 0):
d[i - 1] += step
sum = 0
for s in range(0, hlen - 1, 2):
sum ^= d[s]
if sum == 0:
print(h[i], h[i] + step)
break
d[i] += step
if i != 0:
d[i - 1] -= step
step += 1
横向打印二叉树
Description
二叉树可以用于排序。其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树。
当遇到空子树时,则把该节点放入那个位置。
比如,10 8 5 7 12 4 的输入顺序,应该建成二叉树如下图所示,其中.表示空白。
…|-12
10-|
…|-8-|
…|…|-7
…|-5-|
…|-4
本题目要求:根据已知的数字,建立排序二叉树,并在标准输出中横向打印该二叉树。
Input
输入数据为一行空格分开的N个整数。 N<100,每个数字不超过10000。
输入数据中没有重复的数字。
Output
输出该排序二叉树的横向表示。为了便于评卷程序比对空格的数目,请把空格用句点代替:
Sample Input 1
…|-12
10-|
…|-8-|
…|…|-7
…|-5-|
…|-4
Sample Output 1
10 5 20
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
class DNode:
def __init__(self, data, depth, parent, lchild=None, rchild=None):
self.data = data
self.depth = depth
self.parent = parent
self.lchild = lchild
self.rchild = rchild
class BiTree:
def __init__(self):
self.root = None
def insert(self, value, node=None, depth=0):
if not self.root:
self.root = DNode(value, depth, None)
return
if not node:
node = self.root
depth += 1
if value < node.data:
if not node.lchild:
node.lchild = DNode(value, depth, node)
else:
self.insert(value, node.lchild, depth)
else:
if not node.rchild:
node.rchild = DNode(value, depth, node)
else:
self.insert(value, node.rchild, depth)
def L(n):
return len(str(n))
def centre(node, visit):
if not node:
return
centre(node.rchild, visit)
visit(node)
centre(node.lchild, visit)
def P(node, parents):
if node.parent:
parents.append(node.parent)
P(node.parent, parents)
def TreePrint(node):
flag = ""
parents = []
P(node, parents)
for i in range(len(parents)-1, -1, -1):
p = parents[i]
nodelen = 0
if p.parent:
nodelen+=1
if p.lchild or p.rchild:
nodelen+=1
nodelen+=L(p.data)
flag += "."*nodelen
if i > 0:
# 左右交叉就加竖线,反之用点代替
if node.data < p.data and node.data > parents[i-1].data:
flag += "|"
elif node.data > p.data and node.data < parents[i-1].data:
flag += "|"
else:
flag += "."
if node.parent:
flag += "|-"
flag += str(node.data)
if node.lchild or node.rchild:
flag += "-|"
print(flag)
n = list(map(int, input().split()))
tree = BiTree()
for i in n:
tree.insert(i)
centre(tree.root, TreePrint)
大臣的旅费
Description
很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
Input
输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
城市从1开始依次编号,1号城市为首都。
接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
Output
输出一个整数,表示大臣J最多花费的路费是多少。
Sample Input 1
5
1 2 2
1 3 1
2 4 5
2 5 4
Sample Output 1
135
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
大臣J从城市4到城市5要花费135的路费。
cnt=0
node=0
def dfs(v,k): #进行dfs搜索
global cnt
global node
global vis
if k>cnt:
cnt=k
node=v
for i in range(len(E[v])):
if vis[E[v][i][0]]==False: #没访问过的点都进行一次dfs
vis[E[v][i][0]]=True #进行标记
print(E[v][i][0])
dfs(E[v][i][0],k+E[v][i][1])
vis[E[v][i][0]]=False #回溯
n=int(input())
E=[[] for i in range(n+1)]
vis=[False for i in range(n+1)]
for i in range(n-1):
x,y,z=map(int,input().split())
E[x].append((y,z))
E[y].append((x,z)) #将各个点进行连接,用列表数组的方式进行
vis[1]=True
dfs(1,0) #找到距离1最远的点node
vis[1]=False
cnt=0
vis[node]=True
dfs(node,0) #从node出发,找到最远的点,就是所要求的最远距离
res=0
for i in range(1,cnt+1):
res+=i+10
print(res)
买不到的数目
Description
小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
Input
两个正整数,表示每种包装中糖的颗数(都不多于1000)
Output
一个正整数,表示最大不能买到的糖数
Sample Input 1
4 7
Sample Output 1
17
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
a, b = map(int, input().split())
print(a*b-a-b)
连号区间数
Description
小明这些天一直在思考这样一个奇怪而有趣的问题:
在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:
如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
Input
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。
Output
输出一个整数,表示不同连号区间的数目。
Sample Input 1
4
3 2 4 1
Sample Output 1
7
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 50000;
int a[maxn];
int main(void)
{
int n,cnt=0;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++)
{
int minn=a[i],maxn=a[i];
for(int j=i;j<n;j++)
{
if(a[j]<minn) minn=a[j];
if(a[j]>maxn) maxn=a[j];
if(maxn-minn+1==j-i+1) cnt++;
}
}
printf("%d",cnt);
return 0;
}
测试
农场阳光
Description
X星球十分特殊,它的自转速度与公转速度相同,所以阳光总是以固定的角度照射。
最近,X星球为发展星际旅游业,把空间位置出租给Y国游客来晒太阳。每个租位是漂浮在空中的圆盘形彩云(圆盘与地面平行)。当然,这会遮挡住部分阳光,被遮挡的土地植物无法生长。
本题的任务是计算某个农场宜于作物生长的土地面积有多大。
Input
输入数据的第一行包含两个整数a, b,表示某农场的长和宽分别是a和b,此时,该农场的范围是由坐标(0, 0, 0), (a, 0, 0), (a, b, 0), (0, b, 0)围成的矩形区域。
第二行包含一个实数g,表示阳光照射的角度。简单起见,我们假设阳光光线是垂直于农场的宽的,此时正好和农场的长的夹角是g度,此时,空间中的一点(x, y, z)在地面的投影点应该是(x + z * ctg(g度), y, 0),其中ctg(g度)表示g度对应的余切值。
第三行包含一个非负整数n,表示空中租位个数。
接下来 n 行,描述每个租位。其中第i行包含4个整数xi, yi, zi, ri,表示第i个租位彩云的圆心在(xi, yi, zi)位置,圆半径为ri。
Output
要求输出一个实数,四舍五入保留两位有效数字,表示农场里能长庄稼的土地的面积。
Sample Input 1
10 10
90.0
1
5 5 10 5
Sample Output 1
21.46
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
#include <bits/stdc++.h>
using namespace std;
#define pdd pair<double,double>
#define l first
#define r second
const int N = 100;
double eps = 1e-6;
int n;
double a,b,x[N],y[N],r[N];
double f(double xi){
priority_queue<pdd,vector<pdd>,greater<pdd> > pq;
for(int i = 0;i < n;++i){
double y1 =r[i]*r[i]-(x[i]-xi)*(x[i]-xi);
if(y1 < eps)continue;
y1 = sqrt(y1);
pq.push(pdd(max(0.0,y[i]-y1),min(b,y[i]+y1)));
}
double ret = 0,last = 0;
while(!pq.empty()){
pdd seg = pq.top();pq.pop();
if(seg.l > last)ret += seg.r - seg.l,last = seg.r;
else if(seg.r > last)ret += seg.r - last,last = seg.r;
}
return ret;
}
double simpson(double l,double r){
double mid = (l+r)/2;
return (r-l)*(f(l)+4*f(mid)+f(r))/6;
}
double integral(double l,double r,double crt){
double mid = (l+r)/2.0,L = simpson(l,mid),R = simpson(mid,r);
if(fabs(crt-L-R)<eps)return L+R;
return integral(l,mid,L) + integral(mid,r,R);
}
int main(){
double ctg;
cin >> a >> b >> ctg >> n;
ctg = 1.0/tan(ctg/180.0*M_PI);
for(int i = 0;i < n;++i){
double z;cin >> x[i] >> y[i] >> z >> r[i];
x[i] += z * ctg;
}
priority_queue<pdd,vector<pdd>,greater<pdd> > pq;
for(int i = 0;i < n;++i){
if(r[i]==0)continue;
pq.push(pdd(max(0.0,x[i]-r[i]),min(a,x[i]+r[i])));
}
double last = 0,res = 0;
while(!pq.empty()){
pdd seg = pq.top();pq.pop();
if(seg.l > last)res += integral(seg.l,seg.r,simpson(seg.l,seg.r)),last = seg.r;
else if(seg.r > last)res += integral(last,seg.r,simpson(last,seg.r)),last = seg.r;
}
printf("%.2f\n",a*b-res);
return 0;
}
翻硬币
Description
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:oo*oooo
如果同时翻转左边的两个硬币,则变为:oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作
Input
两行等长的字符串,分别表示初始状态和要达到的目标状态。每行的长度<1000
Output
一个整数,表示最小操作步数。
Sample Input 1
. **********
oo
Sample Output 1
5
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
start=input()
end=input()
n=len(start)
flag=0
judge=[start[i]==end[i] for i in range(n)]#将两个输入的作比较一样则为True,不一样这位False
#print(judge)
for i in range(n-1):#贪心算法只要不一样就反面
if not judge[i]:
judge[i]=True
judge[i+1]=not judge[i+1]
flag+=1
print(flag)
# a = True
# print(a)
# b = not a
# print(b)
错误票据
Description
某涉密单位下发了某种票据,并要在年终全部收回。
每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。
因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。
你的任务是通过编程,找出断号的ID和重号的ID。
假设断号不可能发生在最大和最小号。
Input
要求程序首先输入一个整数N(N<100)表示后面数据行数。
接着读入N行数据。
每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000),请注意行内和行末可能有多余的空格,你的程序需要能处理这些空格。
每个整数代表一个ID号。
Output
要求程序输出1行,含两个整数m n,用空格分隔。
其中,m表示断号ID,n表示重号ID
Sample Input 1
2
5 6 8 11 9
10 12 9
Sample Output 1
7 9
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
n = int(input())
ID = []
for i in range(n):
data = list(map(int, input().split()))
ID = ID + data
# print(ID)
ID.sort()
for i in range(ID[0], ID[-1]):
# for i in ID:
if i in ID:
ID.remove(i)
if i in ID:
n = i
else:
m = i
print(m, n)
带分数
Description
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。
还可以表示为:100 = 82 + 3546 / 197。
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。
Input
从标准输入读入一个正整数N (N<1000*1000)
Output
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
Sample Input 1
100
Sample Output 1
11
Hint
HINT:时间限制:1.0s 内存限制:256.0MB
num = 0
n = int(input())
for i in range(1,n):
j = 1
while True:
x = (n-i)*j
l = int(len(str(x))+len(str(i))+len(str(j)))
s = str(i)+str(x)+str(j)
if l >= 10:
break
else:
if l == 9 and s.__contains__('1') and s.__contains__('2') and s.__contains__('3') and s.__contains__('4'):
if s.__contains__('5') and s.__contains__('6') and s.__contains__('7') and s.__contains__('8') and s.__contains__('9'):
num += 1
j += 1
print(num)