睿抗开发者大赛国赛-24

目录

输入格式:

输出格式:

输入样例:

输出样例:

输入格式:

输出格式:

输入样例:

输出样例:

输入格式:

输出格式:

输入样例:

输出样例:

[猜 - IOI技巧:故意制造错误](#猜 - IOI技巧:故意制造错误)

优化

暴力解法

输入格式:

输出格式:

[输入样例 1:](#输入样例 1:)

[输出样例 1:](#输出样例 1:)

样例解释:

[输入样例 2:](#输入样例 2:)

[输出样例 2:](#输出样例 2:)

输入格式:

输出格式:

输入样例:

输出样例:


在今年的睿抗比赛上,有同学的提交代码如下:

复制代码
public asfiasfgwef12(){
int tsadflas=3;
int masf11233=2;
int[]wasdf1213= new int[10 +1];
int[] vasf124l = new int[10 + I];
int[][] ddasf1234p= new int[masf11233
...

你肯定很奇怪,这看上去代码似乎不像是正常写出来的代码呀?没错,这是这位同学在网络上购买了所谓的"保研综测套餐",商家为逃避赛后查重,给这位同学发去了经过混淆的代码。然而经过技术支持方的努力,这位同学不仅被封禁,与 TA 购买了相同"套餐"的同学也利用技术手段全部查出,目前主办方已向警方报案,这些同学的"保研"梦很有可能会转变为"案底"梦......因此如果你在比赛前也购买了类似的服务,现在迷途知返还来得及------毕竟这个商家起码还做了一些努力,许多商家号称"一对一",实际上将一份代码发给了数十位同学......

回到题目,虽然具体检查的手段无法公开,但我们可以尝试简化再简化的手段来找到被这样混淆的程序。对于给定的大量字符串,你首先要提取出所有的关键词 。一个关键词的定义是:由大写字母、小写字母、数字组成的字符串,并且前后均为非大小写字母及数字(包含开头及换行符)。如以下的字符串:

复制代码
int[] vasf124l = new int[10 + I];

关键词为:intvasf124lnewint10以及I

然后对于所有关键词,你需要计算可疑分数 的和以及关键词平均长度。其中一个关键词的可疑分数如下定义:

  • 如果一个关键词同时包含大写字母、小写字母、数字,则分数 + 5 分;
  • 否则,如同时包含(大写字母、数字)或(小写字母、数字)的,分数 + 3 分;
  • 否则,如同时包含(大写字母、小写字母)的,分数 + 1 分;
  • 其余情况不加分。

对于给定的数据,请你输出所有关键词的可疑分数的和、长度的和以及数量。

输入格式:

输入包含若干行字符串,表示待检测的程序。保证每行字符串的长度不超过 1000(除了一行最后的换行符外),输入总长度不超过 6×104,并且至少有一个关键词。

输出格式:

对于输入的所有字符串,第一行输出可疑分数的和,第二行输出关键词的总长度以及关键词的个数(以避免计算平均值带来的浮点误差),用一个空格隔开。

输入样例:

复制代码
static void nnmMNBkf3kfa(){
    int fefvB4=2;
    int [][]fsdk9A=new int[fefvB4][fefvB4];
    fsdk9A[0][0]=1;
    for (int gfdgsUB3 = 0; gfdgsUB3 < fefvB4; gfdgsUB3++) {
        for (int fdnbXZ8 = 0; fdnbXZ8<fefvB4-gfdgsUB3-1; fdnbXZ8++) {
            fsdk9A[gfdgsUB3][fdnbXZ8+1]=fsdk9A[gfdgsUB3][fdnbXZ8]+gfdgsUB3+fdnbXZ8+2;
            fsdk9A[gfdgsUB3+1][fdnbXZ8]=fsdk9A[gfdgsUB3][fdnbXZ8]+gfdgsUB3+fdnbXZ8+1;
            break;
        }
        break;
    }
}

输出样例:

复制代码
155
276 54
python 复制代码
s=[]

while True:
    temp=input()
    if temp!='':    #直接用''判断是不是空行吧,因为EOF是需要用ctrl来停止
        s+=list(temp.split()) #不是用append,不然列表里面有很多小列表
    else:
        break

'''
while True:
    try:
        s.append(list(input().split()))
    except EOFError:
        #pass
        break
'''
print(s)

#print(ord('A'),ord('Z'),ord('a'),ord('z'),ord('0'),ord('9'))

ans=0

l=0

cnt=0

for i in s:
    k=[0]*3
    for j in i:
        if 65<=ord(j)<=90:
            k[0]=1
        elif 97<=ord(j)<=122:
            k[1]=1
        elif 48<=ord(j)<=57:
            k[2]=1
        if sum(k)==3:
            break

    if sum(k)!=0:
        l+=len(i)
        cnt+=1
        if sum(k)==3:
            ans+=5
        elif (k[0] or k[1]) and k[2]:
            ans+=3
        elif k[0] and k[1]:
            ans+=1
        

print(ans)
print(l,cnt)

写完之后才发现没注意题意:前后均为非大小写字母及数字(包含开头及换行符)

所以不是split,可以用双指针来实现

注意:在while里面通过 try 获取输入,然后 except EOFError 来实现防止非零返回,同时判断是不是空行来break

但是有样例通过的同时有样例报错,所以应该是特判

结果不是特判!

空行不一定结束!!!所以应该只有EOFError一种break方式!!!

复制代码
    if s=='':
        a=1
        #break
    else:

'''
while True:
    temp=input()
    if temp!='':    #直接用''判断是不是空行吧,因为EOF是需要用ctrl来停止
        s+=''.join(list(temp.split())) #不是用append,不然列表里面有很多小列表
    else:
        break
'''

#换行符也是分割符号,所以最好是一行行处理

ans=0
L=0
cnt=0

while True:
    try:
        s=input()
    except EOFError:
        break
    
    if s=='':
        break
    else:
        l=0
        r=0
        k=[0]*3
        while r<len(s): #右拖左式双指针
            if 65<=ord(s[r])<=90:
                k[0]=1
            elif 97<=ord(s[r])<=122:
                k[1]=1
            elif 48<=ord(s[r])<=57:
                k[2]=1
            else:
                if sum(k)!=0:
                    L+=r-l
                    cnt+=1
                    if sum(k)==3:
                        ans+=5
                    elif (k[0] or k[1]) and k[2]:
                        ans+=3
                    elif k[0] and k[1]:
                        ans+=1
                l=r+1
                k=[0]*3

            r+=1 #别忘记往后走

        # 别忘记处理最后一段
        if sum(k)!=0:
            L+=r-l
            cnt+=1
            if sum(k)==3:
                ans+=5
            elif (k[0] or k[1]) and k[2]:
                ans+=3
            elif k[0] and k[1]:
                ans+=1

print(ans)
print(L,cnt)

Xepa Legends 是一个第一人称射击类大逃杀("吃鸡")游戏,每局游戏共有 20 支 3 人小队参加,最后获胜的队伍被称为"捍卫者"。

最近 Xepa Legends 举行了亚太地区南赛区的线上比赛,争夺 7 个前往德国曼海姆参加线下赛的资格,国内共有 14 支队伍参与到了其中。因为比赛十分激烈,直到最后谁进了线下仍有巨大的疑问。小 K 喜欢的国内知名战队 DreamTear 因其队内选手杀马特表现不佳,正好卡在出线分数前后,请你赶紧帮帮小 K,计算一下最后的分数情况,看看他喜欢的战队出线了没有吧!

Xepa Legends 的常规赛共有 30 支队伍参加,被分为三组,进行 N 轮比赛,每轮由三组中的两组组成 20 支队伍的参赛阵容,进行若干场比赛,最后每个队伍会获得一个当轮比赛的排名。

对于每轮比赛,队伍会根据排名获得一个在当轮比赛的赋分

排名 分数
第一名 25 分
第二名 21 分
第三名 18 分
第四名 16 分
第五名 15 分
第六名 14 分
第七名 13 分
第八名 12 分
第九名 11 分
第十名 10 分
第十一名 9 分
第十二名 8 分
第十三名 7 分
第十四名 6 分
第十五名 5 分
第十六名 4 分
第十七名 3 分
第十八名 2 分
第十九名 1 分
第二十名 0 分

给定若干轮比赛队伍获得的当轮比赛排名,请你计算出队伍的赋分,并在若干轮比赛后计算出总赋分,从而最终确定 DreamTear 战队能否进入线下,还是只能耍耍花招了。

例如,

  • DreamTear 战队在第一轮比赛中获得了第 17 名,第三轮比赛中获得了第 11 名,第四轮比赛中获得了第 11 名,那么 DreamTear 战队可获 3 + 9 + 9 = 21 分的赋分;
  • KV 战队在第一轮比赛中获得了第 10 名,第三轮比赛中获得了第 2 名,第四轮比赛中获得了第 6 名,那么他们可获得 10 + 21 + 14 = 45 分的赋分。

注:本题与实际情况无关,所有比赛规则、队伍、队员名称均为虚构。

输入格式:

输入第一行是一个正整数 N (≤20),表示有 N 轮比赛。

接下来有 N 部分输入,每部分是一轮比赛的情况。对每一场比赛,信息共分 20 行,第 i 行(i=1,⋯,20)给出的两个非负整数 c 和 p 表示编号为 c 的队伍在这轮比赛里获得了第 p 名。

数据保证所有给定的情况中,排名永远大于等于 1 且小于等于 20,队伍编号由 1 开始,不超过 30。

输出格式:

输出若干行,按分数从大到小依次输出队伍的编号及该队所有轮次游戏结束后的总分。如分数相同,队伍编号较小的先输出。

注意由于统计的时候比赛可能并没有完全结束,所以每个队伍参加的比赛轮数不一定相同,此时仍然正常计分统计即可。不要输出未参赛的队伍分数。

输入样例:

复制代码
3
1 1
2 2
9 3
6 4
7 5
11 6
3 7
13 8
8 9
16 10
4 11
19 12
17 13
5 14
12 15
15 16
14 17
10 18
20 19
18 20
5 11
10 12
30 13
22 14
1 1
28 20
21 16
26 17
2 2
24 3
4 4
29 5
8 6
7 15
6 7
3 8
9 9
25 10
23 19
27 18
19 20
26 19
27 18
18 17
21 16
12 15
28 14
20 13
17 12
14 11
13 10
23 9
29 8
22 7
30 6
15 5
24 4
25 3
16 2
11 1

输出样例:

复制代码
1 50
2 42
11 39
24 34
16 31
6 29
9 29
25 28
29 27
3 25
4 25
8 25
13 22
30 21
7 20
15 19
22 19
5 15
17 15
14 12
23 12
10 10
12 10
19 8
20 8
21 8
28 6
26 4
27 4
18 3
复制代码
sc=[25,21,18,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]

#print(len(sc))

N=int(input())

s=[[i,0] for i in range(30)]

# 不要输出为参赛的队伍!
vis=[0]*30

for _ in range(N):
    for __ in range(20):
        a,b=map(int,input().split())
        vis[a-1]=1
        s[a-1][1]+=sc[b-1]

s=sorted(s,key=lambda x:(x[1],-x[0]),reverse=True)
                    # 通过元组第二元素 - 实现同分数的小的前

#print(s)

for i in range(30):
    if not vis[s[i][0]]:
        continue #注意不是break!因为可能有人参加了但是是0分
    print(s[i][0]+1,s[i][1])

用 n>2 个不同的个位数字组成一个 n 位数,显然有 n! 个不同的结果。可以证明,这 n! 个数字可以被分为势均力敌 的两组 ------ 即平方和相等、且个数也相等的两组。

本题就请你用程序验证一下这个结论。

因为本题是一道简单题,所以规模很小,只考虑 n≤4 的情况。

输入格式:

输入第一行给出正整数 n(2<n≤4),随后一行给出 n 个不同的、在区间 [1, 9] 内的个位数字,其间以空格分隔。

输出格式:

将所有组成的 n! 个不同的 n 位数分为平方和相等、且个数也相等的两组。但你只需要输出其中一组就可以了。每个数字占一行,共输出 n!/2 行。

注意:解可能不唯一,输出任何一组解就可以。

输入样例:

复制代码
3
5 2 1

输出样例:

复制代码
125
512
251

每个数位上每个数出现概率是一样的,而目标是 (1000*a)**2 + (100*b)**2 + ( 10*c )**2 + d**2

猜 - IOI技巧:故意制造错误

(有时候print特征数也是ioi的一个技巧)

观察了样例的数学特征,发现可以通过错序排列实现

然后提交后过了一半以上的分数,后面我发现我的代码在 n==3 的时候是刚好为 3 个,但是 4 的时候正常来说有 12 个但是我的代码只有 4 个,所以我才我过的是 n==3 的情况,于是我做了个分支然后在 n==4 的时候弄个死循环,果不其然刚才没过的样例就是 4 的,所以我可以再对 4 进行优化

python 复制代码
n=int(input())

if n==3:
    l=list(map(int,input().split()))

    ans=[[0]*n for i in range(n)]#for _ in range(n-1)]

    #for z in range(n-1):
    for i in range(n):
        for j in range(n):
            ans[i][j]=l[(i+j)%(n)]

    for i in ans:
        #for j in i:
        print(''.join(map(str,i)))
else:
    while True:
        print(1)

优化

针对上面的概率论:当n为4时,每个数平分后在各个数位上出现次数为 24 /4 /2 = 3 次

但还是错,看来如果想要拿全分数的话猜还是不稳妥

python 复制代码
n=int(input())
l=list(input().split()) #不用map为int,因为下面也要join

if n==3:
    ans=[[0]*n for i in range(n)]#for _ in range(n-1)]

    #for z in range(n-1):
    for i in range(n):
        for j in range(n):
            ans[i][j]=l[(i+j)%(n)]

    for i in ans:
        #for j in i:
        print(''.join(i))

else:
    vis=[0]*4
    nums=[]
    def dfs(step):
        if step==4:
            nums.append(list(''.join(now)))

        for i in range(4):
            if not vis[i]:
                now[step]=l[i]
                vis[i]=1
                dfs(step+1)
                vis[i]=0
    now=['']*4
    dfs(0)
    m = {d: [0]*4 for d in l}  # 计数表
    result = []
    target_count = 3
    total_required = 4 * 4 * target_count  # 总选中数字数目 = 4位 * 4数字 * 3次 = 48
    
    flag=1
    def backtrack(index, chosen_count):
        global flag
        if flag==0:
            return
        if chosen_count == total_required // 4:  # 选中排列数=48/4=12
            # 输出结果

            for perm in result:
                print(''.join(perm))
            #exit(0)
            flag=0
            return
    
        if index == len(nums):
            return
    
        perm = nums[index]
        # 检查是否可选
        for pos in range(4):
            if m[perm[pos]][pos] == target_count:
                # 超过限制,跳过此排列
                break
        else:
            # 选择该排列
            for pos in range(4):
                m[perm[pos]][pos] += 1
            result.append(perm)
    
            backtrack(index + 1, chosen_count + 1)
    
            # 回溯
            result.pop()
            for pos in range(4):
                m[perm[pos]][pos] -= 1
    
        # 不选该排列,继续
        backtrack(index + 1, chosen_count)
    
    backtrack(0, 0)

暴力解法

表示:排列组合后得到总平方和后,挑选 一半数 使得他们和为总和的一半

python 复制代码
import itertools
import sys


def read_input() -> tuple[int, list[int]]:
    n_value = int(sys.stdin.readline().strip())
    digits_list = list(map(int, sys.stdin.readline().split()))
    return n_value, digits_list


def build_numbers_from_permutations(digits: list[int]) -> tuple[list[int], list[int]]:
    permutations = list(itertools.permutations(digits))
    numbers = [int("".join(map(str, p))) for p in permutations]
    squares = [num * num for num in numbers]
    return numbers, squares


def find_half_subset_with_target_sum(squares: list[int]) -> list[int]:
    total_sum = sum(squares)
    target_sum = total_sum // 2
    total_count = len(squares)
    half_count = total_count // 2

    # Meet-in-the-middle: split into two halves
    mid = total_count // 2
    left = squares[:mid]
    right = squares[mid:]

    # Precompute subset sums for left half: key -> (count, sum), value -> mask
    left_map: dict[tuple[int, int], int] = {}
    for mask in range(1 << len(left)):
        cnt = mask.bit_count()
        current_sum = 0
        idx = mask
        pos = 0
        while idx:
            if idx & 1:
                current_sum += left[pos]
            pos += 1
            idx >>= 1
        key = (cnt, current_sum)
        if key not in left_map:
            left_map[key] = mask

    # Precompute subset sums for right half
    right_map: dict[tuple[int, int], int] = {}
    for mask in range(1 << len(right)):
        cnt = mask.bit_count()
        current_sum = 0
        idx = mask
        pos = 0
        while idx:
            if idx & 1:
                current_sum += right[pos]
            pos += 1
            idx >>= 1
        key = (cnt, current_sum)
        if key not in right_map:
            right_map[key] = mask

    # Search for complementary pairs
    for (cnt_left, sum_left), mask_left in left_map.items():
        cnt_needed = half_count - cnt_left
        sum_needed = target_sum - sum_left
        candidate = right_map.get((cnt_needed, sum_needed))
        if candidate is not None:
            # Reconstruct indices selected from both halves
            selected_indices: list[int] = []

            # Left selected
            for i in range(len(left)):
                if (mask_left >> i) & 1:
                    selected_indices.append(i)

            # Right selected (offset by mid)
            for i in range(len(right)):
                if (candidate >> i) & 1:
                    selected_indices.append(mid + i)

            return selected_indices

    # As per the problem's statement, a solution exists. This line should not be reached.
    return []


def main() -> None:
    n, digits = read_input()
    # Problem guarantees 2 < n <= 4 and digits are distinct in [1, 9]
    numbers, squares = build_numbers_from_permutations(digits)

    selected_indices = find_half_subset_with_target_sum(squares)
    if not selected_indices:
        return

    for idx in selected_indices:
        print(numbers[idx])


if __name__ == "__main__":
    main()

"City 不 City"因为一位外国友人保保熊直播旅游时总是用奇怪的腔调说"好 city,啊!"而走红中国社交网络,成为网络热梗。事实上,有一些叛逆的年轻人在旅行时会刻意避开网红打卡点,选择一些小众的特色地方小城镇,不追求 city,而喜欢说"好 country,啊"。

下面给定各个城镇的旅游热度和城镇间的旅行花销,请你为前来咨询的旅行者规划一条最经济的路线,并且尽可能避开热度很高的网红点。

输入格式:

输入第一行首先给出 4 个正整数:n 和 m(1<n≤103,1≤m≤5n),依次为城镇数量(于是城镇编号从 1 到 n)和城镇间的通路条数;s 和 t 依次为旅行者的出发地和目的地的城镇编号。

随后一行给出 n 个不超过 100 的正整数,依次为 n 个城镇的旅游热度。

再后面是 m 行,每行给出一条通路连接的两个城镇的编号、这条通路的最小花销(其数值为不超过 103 的正整数)。通路是双向的,题目保证任一对城镇间至多给出一条通路。

同一行的数字间均以空格分隔。

输出格式:

题目要求从 s 到 t 的最小花销路线;若这样的路线不唯一,则取途径 城镇的最高旅游热度值最小的那条路线。

在一行中输出从 s 到 t 的最小花销、以及途经城镇的最高旅游热度值(若没有途经的城镇,则热度值为 0)。数值间以 1 个空格分隔,行首尾不得有多余空格。

若从 s 根本走不到 t,则在一行中输出 Impossible

输入样例 1:

复制代码
8 14 7 8
100 20 30 10 50 80 100 100
7 1 1
7 2 2
7 3 1
7 4 2
1 2 1
1 5 2
2 5 1
3 4 1
3 5 3
3 6 2
4 6 1
5 6 1
5 8 1
6 8 2

输出样例 1:

复制代码
4 50

样例解释:

从 7 到 8 的最短路径有 3 条,其中 2 条都经过城镇 1,于是对应的最高旅游热度值是城镇 1 的热度值 100。解路径为 7->2->5->8,途径城镇 2 和 5,对应的最高旅游热度值是城镇 5 的热度值 50。在最短路径长度相等的情况下,取热度值小的解,故输出的热度值为 50。

输入样例 2:

复制代码
3 1 1 2
10 20 30
1 3 1

输出样例 2:

复制代码
Impossible

旅行规划:Dijkstra变种,(成本最小,热度最高)

python 复制代码
import heapq
from collections import defaultdict

def solve_travel_route():
    # 读取输入
    n, m, s, t = map(int, input().split())
    popularity = list(map(int, input().split()))
    
    # 构建邻接表
    graph = defaultdict(list)
    for _ in range(m):
        u, v, cost = map(int, input().split())
        graph[u].append((v, cost))
        graph[v].append((u, cost))
    
    # Dijkstra算法变种:同时记录最小成本和最大热度
    # dist[i] = (最小成本, 途经城镇的最大热度)
    dist = {i: (float('inf'), float('inf')) for i in range(1, n+1)}
    dist[s] = (0, 0)  # 起点成本为0,热度为0
    
    # 优先队列:(成本, 最大热度, 当前节点)
    pq = [(0, 0, s)]
    visited = set()
    
    while pq:
        cost, max_popularity, current = heapq.heappop(pq)
        
        if current in visited:
            continue
            
        visited.add(current)
        
        # 如果到达目标节点,返回结果
        if current == t:
            return cost, max_popularity
        
        # 遍历邻居节点
        for neighbor, edge_cost in graph[current]:
            if neighbor in visited:
                continue
                
            new_cost = cost + edge_cost
            # 更新途经城镇的最大热度(不包括起点和终点)
            new_max_popularity = max_popularity
            if neighbor != t:  # 不包括终点
                new_max_popularity = max(max_popularity, popularity[neighbor-1])
            
            # 如果找到更短的路径,或者路径长度相同但热度更小
            if (new_cost < dist[neighbor][0] or 
                (new_cost == dist[neighbor][0] and new_max_popularity < dist[neighbor][1])):
                
                dist[neighbor] = (new_cost, new_max_popularity)
                heapq.heappush(pq, (new_cost, new_max_popularity, neighbor))
    
    # 如果无法到达目标节点
    return "Impossible"

# 处理输入并输出结果
result = solve_travel_route()
if result == "Impossible":
    print("Impossible")
else:
    cost, max_popularity = result
    print(f"{cost} {max_popularity}")

"消消乐"是以消去方块赢取分数的游戏。这里介绍一种超级简单的玩法:玩家每次按住并拖动鼠标,在屏幕上划出一个矩形,则矩形内的方块就被消去,玩家得到所有被消去的方块的分数之和。

每个方块上的小动物对应不同的得分,例如消去上图中的绿色青蛙得 2 分、消去紫色猫头鹰得 5 分、消去黄色小鸡得 9 分、消去蓝色小牛得 1 分、消去红色狐狸得 3 分、消去棕色小熊得 8 分。有些方块是冰块,消去冰块会被扣分,所以冰块上标注了负分。有些方块是黑洞,玩家的矩形内不能包含任何黑洞,否则所有分数都被黑洞吸走。在一个矩形被消去后,其上方的方块会掉落以填补空缺,而多出的空缺被黑洞填补。

本题请你帮助玩家实现一个基于贪心策略的自动消除程序,每次都争取获得最多的分数,直到无法继续获得更多的分数。

输入格式:

每个输入包含 1 个测试用例。每个测试用例第 1 行给出一个不超过 100 的正整数 N,对应正方形游戏屏幕的高度和宽度。随后 N 行,每行给出 N 个数字,代表对应方块的分数,其中黑洞用 0 表示,小动物用正整数分表示,冰块用负整数分表示。数值均在 [−100,100] 区间内。

输出格式:

每一步的消除策略占一行,格式为:

复制代码
(x1, y1) (x2, y2) 得分

其中 (x1, y1) 为矩形左上角的横纵坐标,(x2, y2) 为矩形右下角的横纵坐标,得分 为消去这个矩形的得分。这里我们将游戏屏幕的左上角坐标定义为 (1,1),右下角坐标定义为 (N,N)。

最后一行给出总分。

注意:当有多个矩形同时对应最大得分时,优先选择 x1 最小的;如果 x1 一样,则优先选择 y1 最小的;如果 y1 也一样,则优先选择 x2 最小的;如果 x2 还是一样,则优先选择 y2 最小的。

输入样例:

复制代码
4
0 2 5 0
9 2 -6 2
-4 1 -4 3
-1 8 0 -2

输出样例:

复制代码
(1, 2) (2, 4) 15
(3, 1) (3, 1) 5
(4, 2) (4, 3) 5
(2, 4) (2, 4) 2
27

如果暴力的话会导致 O(N**4) 肯定会超时(但是应该还是能拿点分),

优化点:

1.Kadane最大字段和算法 或者 用前缀和+最小前缀法

2.黑洞排除:按照没有 0 的连续段分割

通过贪心来break

python 复制代码
import sys

def best_rectangle_greedy(board):
    n = len(board)
    best_sum = None
    best_r1 = best_c1 = best_r2 = best_c2 = -1
    # 枚举上边 r1 和下边 r2 -> O(N^2)
    for r1 in range(n):
        col_sums = [0] * n
        col_has_zero = [False] * n
        for r2 in range(r1, n):
            for c in range(n):
                val = board[r2][c]
                col_sums[c] += val if val is not None else 0
                if board[r2][c] == 0:
                    col_has_zero[c] = True
            # 在每个不含零的连续列段上,用前缀最小值法取最大子段(O(n))
            c = 0
            while c < n:
                if col_has_zero[c]:
                    c += 1
                    continue
                seg_l = c
                while c < n and not col_has_zero[c]:
                    c += 1
                seg_r = c - 1
                m = seg_r - seg_l + 1
                P = [0] * (m + 1)
                for i in range(m):
                    P[i+1] = P[i] + col_sums[seg_l + i]
                min_P = 0
                min_idx = 0
                for j in range(1, m+1):
                    cur_sum = P[j] - min_P
                    left = seg_l + min_idx
                    right = seg_l + j - 1
                    # 平局比较:先列 x1(即 left)最小,再行 y1(即 r1)最小,再列 x2 最小,再行 y2 最小
                    if best_sum is None or cur_sum > best_sum or (
                        cur_sum == best_sum and (
                            left < best_c1 or
                            (left == best_c1 and r1 < best_r1) or
                            (left == best_c1 and r1 == best_r1 and right < best_c2) or
                            (left == best_c1 and r1 == best_r1 and right == best_c2 and r2 < best_r2)
                        )
                    ):
                        best_sum = cur_sum
                        best_r1, best_r2 = r1, r2
                        best_c1, best_c2 = left, right
                    if P[j] < min_P:
                        min_P = P[j]
                        min_idx = j
    if best_sum is None:
        return None
    return (best_sum, best_r1, best_c1, best_r2, best_c2)

def apply_move(board, move):
    score, r1, c1, r2, c2 = move
    n = len(board)
    for r in range(r1, r2+1):
        for c in range(c1, c2+1):
            board[r][c] = None
    for c in range(n):
        new_col = [board[r][c] for r in range(n) if board[r][c] is not None]
        while len(new_col) < n:
            new_col.insert(0, 0)
        for r in range(n):
            board[r][c] = new_col[r]
    return score

def main():
    data = sys.stdin.read().strip().split()
    if not data: 
        return
    it = iter(data)
    n = int(next(it))
    board = [[int(next(it)) for _ in range(n)] for _ in range(n)]
    total = 0
    outputs = []
    while True:
        best = best_rectangle_greedy(board)
        if best is None:
            break
        s, r1, c1, r2, c2 = best
        if s <= 0:
            break
        apply_move(board, best)
        total += s
        # 输出 (x,y) 中 x 为列 (c),y 为行 (r),均为 1-based
        outputs.append(f"({c1+1}, {r1+1}) ({c2+1}, {r2+1}) {s}")
    outputs.append(str(total))
    print("\n".join(outputs))

if __name__ == "__main__":
    main()
相关推荐
0wioiw01 小时前
Android-Kotlin基础(Jetpack③-LiveData)
android·开发语言·kotlin
java1234_小锋2 小时前
一周学会Matplotlib3 Python 数据可视化-坐标轴 (Axis)
开发语言·python·信息可视化·matplotlib·matplotlib3
奶油话梅糖2 小时前
【网络自动化】利用Python脚本与计划任务,实现H3C/HPE设备配置无人值守备份
网络·python·自动化
无影无踪的青蛙2 小时前
macOS用户崩溃瞬间:当我发现电脑里有8个Python版本…
python
小苏兮2 小时前
飞算JavaAI深度解析:专为Java生态而生的智能引擎
java·开发语言·人工智能·java开发·飞算javaai炫技赛
十里桃花ღ2 小时前
Python 图像处理库Pillow
python
OAK中国_官方3 小时前
使用OAK相机实现智能物料检测与ABB机械臂抓取
人工智能·python·边缘计算·深度相机
Bdygsl3 小时前
前端开发:JavaScript(6)—— 对象
开发语言·javascript·ecmascript
amazinging3 小时前
北京-4年功能测试2年空窗-报培训班学测开-第七十一天-面试第二天
python·学习·面试
babytiger3 小时前
我的c#用到Newtonsoft.Json.dll,Fleck.dll这两个dll能否打到一个exe 中,而不是一起随着exe拷贝
开发语言·c#·json