目录
一、纸张尺寸
题目:
代码:
s=input()
n=int(s[1])
l=1189 #长
w=841 #宽
while n!=0:
d=l//2 #需要使用d来记录l//2后的值,防止l被覆盖
l=max(d,w)
w=min(d,w)
n=n-1
print(l)
print(w)
二、数位排序
题目:
思路:纯暴力写法(我也没有想到居然没有超时),创建一个二维数组,记录每个数所对应的元素,例如a[1]=[1,10,100],a[2]=[2,11,20] 。然后枚举这个二维数组找到指定位置的数。
代码:
n=int(input())
m=int(input())
a=[[]for i in range(55)]#记录每个数对应的元素
for i in range(n+1):
x=i
sum=0
while x!=0:
sum+=x%10
x=x//10
a[sum].append(i)
res=0
sum=-1
for i in a:
for j in i:
sum+=1
if sum==m:
res=j
break
if res!=0:
break
print(res)
三、蜂巢
题目:
思路:可以建立如下所示的坐标,然后有六个方向,可以用一个列表存储每个方向走一步时,x,y坐标的变化。
1.如果B在C的一三象限,则以第一象限为例:C应该先向3方向走,向3方向走时:x,y坐标每次都各加1,因此最后相当于算的x,y坐标的最大值。
2. 如果B在C的二四象限,Y则以第二象限为例,C先向X轴走,然后向Y轴方向走到达B的距离最近,也就是以C为原点时,B的坐标和(绝对值)
代码:
#坐标转换
def change(d,p,q):
x=p*dir[d][0]+q*dir[(d+2)%6][0]
y=p*dir[d][1]+q*dir[(d+2)%6][1]
return x,y
d1,p1,q1,d2,p2,q2=map(int,input().split())
dir=[(-1,-1),(-1,0),(0,1),(1,1),(1,0),(0,-1)]#新建立坐标系后,6个方向每走一步对应的x,y的变化
x1,y1=change(d1,p1,q1)
x2,y2=change(d2,p2,q2)
#相当于以(x2,y2)为原点
tx=x1-x2
ty=y1-y2
#(x1,y1)在一三象限
if tx*ty>0:
print(max(abs(tx),abs(ty)))
else:
print(abs(tx)+abs(ty))
四、消除游戏
题目:
思路:这个题中涉及字符串的删除,我们应该首先想到双链表来减少时间复杂度。
双链表的删除:
#双链表的删除 def remove(i): r[l[i]],l[r[i]]=r[i],l[i]
除了使用双链表,这题还有一个关键是每一次需要检查的字符是上一次删除字符的相邻字符。因此,我们可以使用两个列表记录每次删除的字符和需要检测的字符,除此之外还需要一个列表标记该位置字符串是否已经删除,避免删除时重复删除(因为是同时删除ai-1、ai或者ai、ai+1,ai可能被重复删除)和下次检测字符列表中加入的是已经删除的字符(在向下次检测列表加入字符时,加入的是被删除字符的左右字符,但被删除字符的左右字符也可能被删除)
几点注意:
1. 输入的字符串前后可以加一个"#"作为边界,在检测是否需要删除时,不操作。
2. 双链表中存储的是字符串的下标,而不是字符串的值
3. 存储需要检测的列表和删除的列表每次操作都要初始化。(代码中deir.pop()相当于就把deir中的元素全取出删除了)
代码:
def check(i):
if s[l[i]]=='#' or s[r[i]]=='#':#超出边界不进行判断
return
if s[i]==s[l[i]] and s[i]!=s[r[i]]:
deir.append(i)
deir.append(r[i])
if s[i]!=s[l[i]] and s[i]==s[r[i]]:
deir.append(i)
deir.append(l[i])
#双链表的删除
def remove(i):
r[l[i]],l[r[i]]=r[i],l[i]
s='#'+input()+'#'
n=len(s)-2
l=[0 for i in range(n+2)]#左节点
r=[0 for i in range(n+2)]#右节点
st=[0 for i in range(n+2)]#标记
deir=[]#需要删除的
#初始化双联表
for i in range(1,n+1):
l[i],r[i]=i-1,i+1
for i in range(1,n+1):
check(i)
while len(deir)!=0:
temp=[]#下一次需要检查的位置
for i in range(len(deir)):
t=deir.pop()
if st[t]==0:
st[t]=1
#下一次需要检查的是上一次删除的左右节点
temp.append(l[t])
temp.append(r[t])
remove(t)
for i in temp:
if st[i]==0:
check(i)
f=False
for i in range(1,n+1):
if st[i]==0:
f=True
print(s[i],end="")
if f==False:
print("EMPTY")
五、全排列的价值
思路:DP三部曲
1. 集合含义dp[i]:所有前i个数全排列的价值
2. 需要求max/min/count:数量问题
3. 集合划分(看最后一个状态):dp[i]=dp[i-1]*i+(i-1)!*sum[i] sum[i] =(i-1)*i//2
第i个数有i中放置的方法:
(1)i在所有数最前面的时候,因为i本身就比后面的数大,所有并不会提高排列价值:
dp[i]=dp[i-1]
(2)i在第一个数后面的时候,因为i一定比第一个数大,所以价值+1,而前i项一共有(i-1)!的排列方式,因此总价值增加(i-1)*1:
dp[i]=dp[i-1]+(i-1)*1
(i)i在第i-1个数后面的时候,因为i一定比第i-1个数大,所以价值+(i-1),而前i项一共有(i-1)!的排列方式,因此总价值增加(i-1)*(i-1):
dp[i]=dp[i-1]+(i-1)*(i-1)
计算阶乘:
import math #计算5! x=math.factorial(5)
零零碎碎:之前一直写DP的题目,但还是没有想到使用DP...../(ㄒoㄒ)/~~ 。因为看到这道题的时候,想去找里面的数学规律,但想不出来....
不过,使用DP还是会超时/(ㄒoㄒ)/~~(我不理解,好不容易用上学的算法模板,还超时),可以拿70%的分。
代码
import math
mod=998244353
n=int(input())
dp=[0 for i in range(n+1)]
sum=[0 for i in range(n+1)]#存和
fact=[1 for i in range(n+1)]#存阶乘
for i in range(1,n+1):
# x=math.factorial(i-1)
# dp[i]=(dp[i-1]*i+x*i*(i-1)//2)%mod
dp[i]=(dp[i-1]*i+fact[i-1]*sum[i-1])%mod
sum[i]=sum[i-1]+i
fact[i]=fact[i-1]*i
print(dp[n]%mod)
六、最优清零策略
题目:
思路: 这道题主要要注意两点
1. 在清除时每个区间的清除顺序是不影响最后的结果。需要把所有区间清除,如果使用一个列表存储每个位置的值,则所有的清除区间的次数加上列表最后值(需要每次减1,也就是操作1)的总和就是最后结果。
2. 优化方法:区间删除时,可以删除的最大次数是这个区间内的最小值;在下一次寻找区间时,是从上一个区间最小值的下一个数开始寻找。
代码:
#与不同区间清零的先后次序无关,先按顺序把所有区间清完,然后再计算单个的
n,k=map(int,input().split())
l=list(map(int,input().split()))
res=0
i=0
while i+k-1<n:
m=min(l[i:i+k])#找到区间中最小的数,代表需要减去的次数为它
res+=m
for j in range(i,i+k):
l[j]-=m
if l[j]==0:
i=j #优化:下一次直接从为0后面的区间开始找
i+=1
res+=sum(l)
print(res)