csp 2021

202104-1 灰度直方图

解答:

import sys
n,m,L = map(int,sys.stdin.readline().split())
result = [0]*L
for i in range(n):
    list_gray = map(int,sys.stdin.readline().split())
    for j in list_gray:
        result[j] += 1
result = [str(i) for i in result]
print(" ".join(result))

202109-1 数组推导

解答:

n = eval(input())
B = list(map(int,input().split()))
sum_min = sum_max = B[0]
for i in range(1,n):
    sum_max += B[i]
    if B[i] != B[i-1]:
        sum_min += B[i]
print(sum_max)
print(sum_min)

    202104-2     邻域均值

解答:

70分解法:

import sys
n,L,r,t = map(int,sys.stdin.readline().split())
test = [[]]*n
for i in range(n):
    test_member = list(map(int,sys.stdin.readline().split()))
    test[i] = test_member
result = 0
for i in range(n):
    for j in range(n):
        left = (j-r) if (j-r)>0 else 0
        right = (j+r) if (j+r)<(n-1) else (n-1)
        up = (i-r) if (i-r)>0 else 0
        down = (i+r) if (i+r)<(n-1) else (n-1)
        sum_num = 0
        for x in range(left,right+1):
            for y in range(up,down+1):
                sum_num += test[x][y]
        avg_num = sum_num/((right-left+1)*(down-up+1))
        if avg_num <= t:
            result += 1
print(result)

 100分:

import sys
n,L,r,t = map(int,sys.stdin.readline().split())
test = [[0]*(n+1) for i in range(n+1)]
test_space = [[0]*(n+1) for i in range(n+1)]
num = 0
for i in range(1,n+1):
    test_member = list(map(int,sys.stdin.readline().split()))
    test[i][1:] = test_member
    for j in range(1,n+1):
        test_space[i][j] = test_space[i][j-1] + test_space[i-1][j] - test_space[i-1][j-1] + test[i][j]
for i in range(1,n+1):
    for j in range(1,n+1):
        a = max(1,i-r)
        b = max(1,j-r)
        c = min(n,i+r)
        d = min(n,j+r)
        s = test_space[c][d] - test_space[c][b-1] - test_space[a-1][d] + test_space[a-1][b-1]
        sum_num = (c-a+1) * (d-b+1)
        if s <= t * sum_num:
            num += 1
print(num)

参考:https://blog.csdn.net/qq_51314467/article/details/123215923

 

注意多添加一行一列,为的是1:下标好对应。2:原来的第一行第一列不用判断边界,和其他位置操作一样。

202109-2 非零段划分 

解答:70分

import sys
n = eval(sys.stdin.readline())
list1 = list(map(int,sys.stdin.readline().split()))
test = list(set(list1))
# print(test)
test.sort()
max_result = 0
for i in test:
    temp = list1
    for j in range(n):
        if temp[j] == i:
            temp[j] = 0
    if temp[0] != 0:
        temp_num = 1
    else:
        temp_num = 0
    for j in range(1,n):
        if temp[j-1] == 0 and temp[j] != 0:
            temp_num += 1
    max_result = max(temp_num,max_result)
print(max_result)

 尝试优化,还是70

import sys
n = eval(sys.stdin.readline())
list1 = list(map(int,sys.stdin.readline().split()))
test = list(set(list1))
# print(test)
test.sort() #sort改变列表,sorted有返回值

#数初始的非零段
if list1[0] != 0:
    temp_num = 1
else:
    temp_num = 0
for j in range(1,n):
    if list1[j-1] == 0 and list1[j] != 0:
        temp_num += 1

max_result = temp_num
for i in test:
    if i != 0:
        for j in range(n):
            if list1[j] == i:
                list1[j] = 0
                if j == 0:
                    if list1[1] == 0:
                        temp_num -= 1
                elif j == (n-1):
                    if list1[n-2] == 0:
                        temp_num -= 1
                else:
                    if list1[j-1] == 0 and list1[j+1] == 0:
                        temp_num -= 1
                    elif list1[j-1] != 0 and list1[j+1] != 0:
                        temp_num += 1
        if temp_num > max_result:
            max_result = temp_num
    # if temp[0] != 0:
    #     temp_num = 1
    # else:
    #     temp_num = 0
    # for j in range(1,n):
    #     if temp[j-1] == 0 and temp[j] != 0:
    #         temp_num += 1
    # max_result = max(temp_num,max_result)
print(max_result)

https://blog.csdn.net/qq_47780707/article/details/123436831

参考如上博客

补充了前缀和和差分的知识,但还是看不懂博客中代码一做法,代码二给了一些启发:

我原来的做法是每次选择一个数字变0后,判断两边数字来看非零区间是否增加(如上我写的的代码二)。但是变零的过程也要循环,博客中代码二可以一次循环判断并记录,记录的是差分。

还有一点巧妙的是,从大数开始记录,即p一开始为大数,区间状态为0个区间(都为0)。

注意,下面这个代码从小数记录是错的。而且下面这个代码0分的原因是没有去重。例如【23332】的3同时变化,所以看做一个数

#从大数开始记录
import sys
n = eval(sys.stdin.readline())
list1 = list(map(int,sys.stdin.readline().split()))
# test = list(set(list1))
# test.remove(0)
# test.sort() #sort改变列表,sorted有返回值
# print(test)

#数初始的非零段
# if list1[0] != 0:
#     temp_num = 1
# else:
#     temp_num = 0
# for j in range(1,n):
#     if list1[j-1] == 0 and list1[j] != 0:
#         temp_num += 1

m = max(list1)  #最大的p值
step = [0]*(m+1)    #记录变化值
if list1[0] > list1[1]:
    step[list1[0]] += 1
if list1[-1] > list1[-2]:
    step[list1[-1]] += 1
for x in range(1,n-1):
    if list1[x-1] < list1[x] and list1[x] > list1[x+1] and list1[x] != 0:
        step[list1[x]] += 1
    elif list1[x-1] > list1[x] and list1[x] < list1[x+1] and list1[x] != 0:
        step[list1[x]] -= 1

max_result = 0
temp_num = 0
for i in step[::-1]:
    temp_num += i
    if temp_num > max_result:
        max_result = temp_num
# print(step)
print(max_result)

100分

#从大数开始记录
import sys
n = eval(sys.stdin.readline())
list1 = list(map(int,sys.stdin.readline().split()))

#去重
b = []
b.append(list1[0])
for i in range(1,n):
    if list1[i-1] != list1[i]:
        b.append(list1[i])

m = max(b) #用来存储最大的p值
step = [0]*(m+1)    #记录变化值

b.insert(0,0)
b.append(0) #方便比较

for x in range(1,len(b)-1):
    if b[x-1] < b[x] and b[x] > b[x+1]:
        step[b[x]] += 1
    elif b[x-1] > b[x] and b[x] < b[x+1]:
        step[b[x]] -= 1

max_result = 0
temp_num = 0
for i in step[::-1]:
    temp_num += i
    if temp_num > max_result:
        max_result = temp_num
# print(step)
print(max_result)

    202112-1 序列查询

 

解答:

n,N = map(int,input().split())
list_price = list(map(int,input().split()))
number_list = [0]*n
result = 0
for i in range(1,n):
    number_list[i] = list_price[i] - list_price[i-1]
    result += i * number_list[i]
result += n * (N-list_price[-1])
print(result)

202203-5 博弈论与石子合并

解答:

参考:

https://blog.csdn.net/qq_23096319/article/details/125192426

https://blog.csdn.net/Wu_L7/article/details/126319300

思想:

小c目的是使石头数量最少,而小z目的是是使石头数量最多。而且假定两个人都绝顶聪明。

因为小c每次只能丢弃左右两堆石头,且因为他聪明,他的步数总是最优,而不是简单地比较两边石头的大小,如果能确定最终的结果,那么小c的每一步都是固定的。如果小z只考虑合并最大的石头堆,且石头堆不处于剩余石头堆的中间位置,那么合并的石头堆可能会被丢弃,无论进行几步合并,小c丢弃一次就都丢弃了,小z相当于替小c进行丢弃操作。所以小z还要保证最后的石头堆处于安全位置。所以小z的任务是在小c这个限制条件下的最大值。

下面分情况讨论:

情况1:

如果是偶数堆,且小z先手,他就合并最中间,这样保证自己在安全位置,距离两边距离都一样。

下面该小c,小c取出一堆石头后,小z再次处于危险位置,这时候小z还是选择合并剩余石头的中间两个,最终合并的一个石头堆是由原来位置连续的石头堆组成的——为什么呢?

这里注意小z很聪明,他知道自己合并的堆不在安全位置就会被丢掉。那么小z的做法就是每次合并安全位置。每个人的步数是有限的,小z为(n/2+1),小c为(n/2)步。小c也知道小z聪明,每次都处于安全位置,他只能每次从两边剥离石头堆(并不是哪个多丢哪个)。虽然小c对小z每次选择安全位置的行为无能为力,但是他知道小z的选择是连续的,小c的做法就是使这个连续位置最小。

所以这种情况就是求最小的,长度为(n/2+1)的连续石头堆的和。如果小z不聪明,选择的位置不连续,会比这个值更小。如果是奇数堆,且小c先手,也属于情况1。

情况2:

如果是奇数堆,且小z先手。此时中间位置只有一堆石头,无论我们合并哪一边,都处于危险位置,都逃不过被丢弃,小c最后只会留下两个中较小的那个。这种情况两人步骤都为(n/2)步。这样就不能保证合并的石头堆留到最后,如果这些石头堆中有大于(n/2)个x,则小c怎么也扔不完x,所以小z的目标是找到大于(n/2)个的最大的x。

代码:60分

#c希望少,k=0
n,k = map(int,input().split())
list_stone = list(map(int,input().split()))
# sum_list_stone = sum(list_stone)
sum_list_stone = 10**9
list_stone_record = [0]*n
list_stone_record[0] = list_stone[0]
for i in range(1,n):
    list_stone_record[i] = list_stone[i] + list_stone_record[i-1]
# print(list_stone_record)

def fun1(num):
    result = list_stone_record[num-1]
    for i in range(1,n-num+1):
        result = min(result,list_stone_record[i+num-1]-list_stone_record[i-1]) #sum(list_stone[i:i+num]
    return int(result)

def check(x,num):
    sumtemp = 0
    tempnum = 0
    for i in range(len(list_stone)):
        sumtemp += list_stone[i]
        if sumtemp >= x:
            sumtemp = 0
            tempnum += 1
    return tempnum > num

def fun2(num):
    temp = 1
    L = 1
    R = sum_list_stone
    while( L <= R):
        mid = (L + R)//2
        if(check(mid,num)): #即使折半法找到了,也要继续找最大的
            temp = mid
            L = mid + 1
        else:
            R = mid -1
    return int(temp)

if n%2 == 0 and k == 1:
    num = n//2 + 1
    print(fun1(num))
elif n%2 == 1 and k == 0:
    num = (n-1) // 2 + 1
    print(fun1(num))
elif n%2 == 0 and k == 0:
    num = n//2
    del list_stone[0]
    del list_stone[-1]
    print(fun2(num-1))
else:
    num = n//2
    print(fun2(num))


202112-2 序列查询新解

 解答:

70分

n,N = map(int,input().split())
A = list(map(int,input().split()))
# A.insert(0,0)
r = int(N/(n+1))
result = 0

start = 0   #记录每组计算的开始坐标
the_index = 0
for A_member in A:
    for i in range(start,A_member):
        g_i = int(i/r)
        f_i = the_index
        # print(g_i,f_i)
        result += abs(g_i - f_i)
    the_index += 1  #下一次计算的下标和开始
    start = A_member
for i in range(A[-1],N):
    g_i = int(i/r)
    result += abs(g_i - the_index)
print(result)

100分:参考

CSP-2序列查询新解_Vanghua的博客-CSDN博客

n,N = map(int,input().split())
A = list(map(int,input().split()))
# A.insert(0,0)
r = int(N/(n+1))
result = 0


def getSum(start,end,value):
    gMax = int((end - 1) / r)
    gMin = int(start/r)
    if gMax <= value:
        return getSum_f(start,end,value) -(getSum_g(end - 1) - getSum_g(start - 1))
    elif gMin >= value:
        return getSum_g(end - 1) - getSum_g(start - 1) - getSum_f(start,end,value)
    else:
        for i in range(gMin,gMax):
            if i >= value:
                change_index = i * r
                sum1 = getSum_f(start,change_index,value) - (getSum_g(change_index - 1) - getSum_g(start - 1))
                sum2 = ( getSum_g(end - 1 ) - getSum_g(change_index - 1) ) - getSum_f(change_index,end,value)
                return sum1 + sum2

def getSum_f(start,end,value):
    return (end - start) * value

def getSum_g(index):

    n2 = (index + 1) % r
    n3 = index - n2
    n1 = int(n3 / r)

    sum1 = n1 * (n1 + 1) // 2 * r
    if n2 != 0:
        sum2 = n2 * int(index/r)
    else:
        sum2 = 0

    return sum1 + sum2

start = 0   #记录每组计算的开始坐标
the_value_f = 0
for A_member in A:
    # if A_member == 3:
    #     print(start,A_member,the_value_f)
    result += getSum(start,A_member,the_value_f)
    start = A_member
    the_value_f += 1

result += getSum(start,N,the_value_f)

print(result)

注意下标的处理。

注意两个思想:1.因为在每段中,一个函数固定值,另一个函数是递增函数,所以考虑比较边界值,分情况讨论有三种情况

2.对第二个函数分段求比较难求,利用前缀和数列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值