2021年寒假每日一题,2017~2019年的省赛真题。
本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。
后面的每日一题,每题发一个新博文,请大家每天看博客蓝桥杯专栏: https://blog.csdn.net/weixin_43914593/category_10721247.html

提供C++、Java、Python三种语言的代码。

文章目录

  • 1、题目描述
  • 2、题解
    • 2.1 一维差分+二分法
    • 2.2 二维差分、三维差分
  • 3、C++代码
  • 4、对敲和测试
    • 4.1 用python写个暴力法的对敲代码
    • 4.2 用python产生测试数据
  • 5、对拍测试

2018省赛A组第7题“三体攻击” ,题目链接:
http://oj.ecustacm.cn/problem.php?id=1364
https://www.dotcpp.com/oj/problem2275.html

1、题目描述


  一个 A × B × C A×B×C A×B×C,即A层B行C列的立方体,第i层j行k列(记为战舰 ( i , j , k ) (i,j,k) (i,j,k))的生命值是 d ( i , j , k ) d(i,j,k) d(i,j,k)。
  三体人队它发起m轮“立方体攻击”,每次攻击对一个小立方体的所有战舰都造成同等伤害。具体地,第t轮攻击用7个参数 l a t , r a t , l b t , r b t , l c t , r c t , h t la_t, ra_t, lb_t, rb_t, lc_t, rc_t, h_t lat​, rat​, lbt​, rbt​, lct​, rct​, ht​ 描述,即所有位于 i ∈ [ l a t , r a t ] , j ∈ [ l b t , r b t ] , k ∈ [ l c t , r c t ] i ∈ [la_t, ra_t],j ∈ [lb_t, rb_t],k ∈ [lc_t, rc_t] i ∈ [lat​, rat​],j ∈ [lbt​, rbt​],k ∈ [lct​, rct​] 的战舰 ( i , j , k ) (i, j, k) (i, j, k) 会受到 h t h_t ht​ 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
  问第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
  数据规模: A × B × C ≤ 1 0 6 , m ≤ 1 0 6 , 0 ≤ d ( i , j , k ) , h t ≤ 1 0 9 A × B × C ≤ 10^6, m ≤ 10^6, 0 ≤ d(i, j, k), h_t ≤ 10^9 A × B × C ≤ 106, m ≤ 106, 0 ≤ d(i, j, k), ht​ ≤ 109。
  时间限制:2s。
  内存限制:256M。


2、题解

  (比较深入地搞过算法竞赛的队员一看就知道,这是一个三维差分的模板题。)

  首先看看数据规模,有 n = 1 0 6 n=10^6 n=106个点, m = 1 0 6 m=10^6 m=106次攻击,如果用暴力,统计每次攻击后每个点的生命值,那么复杂度是 O ( m n ) O(mn) O(mn)的,题目时限是2s,必然会超时。
  暴力法的编码很简单,可以用来对敲,检验正式代码的正确性。
  题目给的是三维空间,我们可以先思考一维、二维情况,看是否有启发。

2.1 一维差分+二分法

  一维,即所有战舰排成一条线。
  每次把一个区间内的所有元素(战舰生命值)减去一个相同的 h t h_t ht​值,是经典的“一维区间修改问题”,可以用“差分数组”来处理数据。
  注:“差分数组”的概念,请看博文树状数组的“4. 区间修改 + 单点查询”、“5、差分数组”中的讲解。请先完全搞懂“差分数组”,再往下看。
  “差分数组”有什么好处呢?一次修改一个区间,如果用暴力法,需要修改区间内每个元素的值,复杂度O(n),但是用差分数组,就只需要修改区间的两个端点,复杂度O(1)。m次修改的总复杂度只有O(m)。
  但是光用差分数组并不能解决问题。因为在差分数组上查询区间内的每个元素是否小于0,需要用差分数组来计算区间内每个元素的值,复杂度是O(n)的。合起来的总复杂度是O(mn)的,其实跟暴力法的复杂度一样。
  这就要加上第二个算法:二分法。从第1次修改到第m次修改,肯定有一次修改是临界点。在这次修改前,没有负值(战舰爆炸);在这次修改后,出现了负值,且后面一直有负值。那么对m进行二分,就能在O(logm)次内找到这个临界点,这就是答案。
  具体的操作步骤是:
  1、读取输入:存储n=A × B × C个点(战舰)的生命值;存储m次修改。
  2、第1次二分,从最大的m开始:判断做m次修改后是否产生负值。过程是:先做m次差分修改,得到一个差分数组,复杂度O(m);然后根据这个差分数组计算每个战舰的值,看是否有负数,复杂度O(n)。总复杂度O(m+n)。
  3、重复以上二分操作,直到找到临界修改的次数。
  一共做O(logm)次二分,总复杂度O((m+n)logm),完美完成编码任务,AC

2.2 二维差分、三维差分

  同理有二维差分和三维差分。二维差分有4个区间端点;三维差分有8个区间端点。复杂度也都是O((m+n)logm)的,不过常数要大4倍、8倍。
  罗老师还没有写二维和三维差分的解析。可以参考下面的博文:
  二维差分:https://blog.csdn.net/justidle/article/details/104506724
  三维差分:
https://blog.csdn.net/weixin_44716674/article/details/105577862
https://blog.csdn.net/weixin_43738764/article/details/105553072
  最后是倪文迪的话:“这道题主要考察三维差分以及二分。我们可能处理过二维差分,通过局部单点的修改以及求前缀和来较为高效地求解某一特定时刻的状态。三维差分则是将对应立方体的八个点修改,原理类似。而本题中二分的是出现负值的id,因为我们只需求解第一次出现负值的编号。”

3、C++代码

  下面的C++代码清晰地重现了上面的解释。如有疑问,请看注释。

//cpp文件取名为 good.cpp
#include<bits/stdc++.h>
using namespace std;
int A,B,C,n,m;
int d[1000005];   //存储舰队生命值
int D[1000005];   //三维差分数组(压维);同时也用来计算每个点的攻击值
int lat[1000005],rat[1000005];  //存储攻击
int lbt[1000005],rbt[1000005];
int lct[1000005],rct[1000005];
int ht[1000005];
int num(int i,int j,int k) {    //小技巧:压维,把三维坐标[i][j][k]转为一维的((i-1)*B+(j-1))*C+(k-1)+1if (i>A || j>B || k>C) return 0;return ((i-1)*B+(j-1))*C+(k-1)+1;
}
bool check(int x) {              //检查经过x次攻击后是否有战舰爆炸for (int i=1; i<=n; i++) D[i]=0;for (int i=1; i<=x; i++) {    //三维差分数组:三维有8个区间端点D[num(lat[i],  lbt[i],  lct[i])]   += ht[i];D[num(rat[i]+1,lbt[i],  lct[i])]   -= ht[i];D[num(lat[i],  rbt[i]+1,lct[i])]   -= ht[i];D[num(lat[i],  lbt[i],  rct[i]+1)] -= ht[i];D[num(rat[i]+1,rbt[i]+1,lct[i])]   += ht[i];D[num(lat[i],  rbt[i]+1,rct[i]+1)] += ht[i];D[num(rat[i]+1,lbt[i],  rct[i]+1)] += ht[i];D[num(rat[i]+1,rbt[i]+1,rct[i]+1)] -= ht[i];}for (int i=1; i<=A; i++)for (int j=1; j<=B; j++)for (int k=1; k<C; k++)D[num(i,j,k+1)] += D[num(i,j,k)];  //用差分数组计算出每个点的攻击值for (int i=1; i<=A; i++)for (int k=1; k<=C; k++)for (int j=1; j<B; j++)D[num(i,j+1,k)] += D[num(i,j,k)];for (int j=1; j<=B; j++)for (int k=1; k<=C; k++)for (int i=1; i<A; i++)D[num(i+1,j,k)] += D[num(i,j,k)];for (int i=1; i<=n; i++)if (D[i]>d[i])return true; //攻击值大于生命值return false;
}
int main() {scanf("%d%d%d%d", &A, &B, &C, &m);n=A*B*C;for (int i=1; i<=n; i++) scanf("%d", &d[i]);for (int i=1; i<=m; i++) scanf("%d%d%d%d%d%d%d",&lat[i],&rat[i],&lbt[i],&rbt[i],&lct[i],&rct[i],&ht[i]);int L=1,R=m;      //经典的二分写法while (L<R) {     //对m进行二分,找到临界值int mid=(L+R)>>1;if (check(mid)) R=mid;else L=mid+1;}printf("%d\n", R);  //打印临界值return 0;
}

4、对敲和测试

  正好用这个题目练练对敲和测试。
  参考博文:Python在竞赛中的应用-测试数据的构造与对拍https://blog.csdn.net/weixin_43914593/article/details/111385152

4.1 用python写个暴力法的对敲代码

  暴力代码很容易写。下面的代码取名为baoli.py

A,B,C,m = map(int,input().split())ship=[]
for i in range(A):sublist=[]for j in range(B):sublist.append([0]*C)ship.append(sublist)life=list(map(int,input().split()))
v=0
for i in range(A):for j in range(B):for k in range(C):ship[i][j][k]=life[v]  #战舰生命值v += 1
num = m
for attacknum in range(1,m+1):la, ra, lb, rb, lc, rc, ht = map(int,input().split())for i in range(la-1,ra):for j in range(lb-1,rb):for k in range(lc-1,rc):ship[i][j][k] -= htif ship[i][j][k]<0:print(attacknum)exit()

4.2 用python产生测试数据

  写一个py文件,按题目要求的格式产生测试数据。文件名取为test.py
  代码好写,参数不好设置。如果有读者有如何设置参数的心得,请告诉我,分享给大家。

#test.py
from random import *
N = 1e4                       #自定义一个合适的值
HT = 1e9                      A = randint(1,N)
B = randint(1,N//A)
C = randint(1,N//A//B)
m = randint(1,N)
print( A,B,C,m)               #第一行for i in range(A*B*C-1):      #第二行print (randint(HT//1000,HT),end=' ')    #生命值设大一些
print (randint(HT//1000,HT))for i in range(m):                #后面m行,每行7个lat = randint(1,A)rat = randint(lat,A)          #注意:rat比lat大lbt = randint(1,B)rbt = randint(lbt,B)lct = randint(1,C)rct = randint(lct,C)ht  = randint(1,HT/100000)     #攻击值设小一些print (lat,rat,lbt,rbt,lct,rct,ht)

5、对拍测试

  本机在windows下测试。写一个循环测试对拍的bat文件,取名为aa.bat,它的工作是:
  先用test.py产生测试数据,存到data.in文件中。
  运行对拍代码test.py,读输入数据,把输出数据输出到文件py.out
  运行好代码good.cpp,输出到文件good.out
  用fc命令比较两个输出py.outgood.out是否完全一致。
  循环测试多次。
  注意其中的path是作者机器的目录,和读者的path不同。

@echo off
set path=C:\MinGW\bin
g++ -o good.exe good.cpp
:loop
set path=C:\Users\hp\AppData\Local\Programs\Python\Python39
python test.py >data.in
good.exe <data.in >good.out
python baoli.py <data.in >py.out
set path=C:\Windows\System32
fc py.out good.out
good.exe <data.in
if errorlevel == 1 pause
goto loop

  为了看清答案,还打印了每次测试的输出。在windows的cmd里执行aa.bat,结果如下:

D:\cpp>aa.bat
正在比较文件 py.out 和 GOOD.OUT
FC: 找不到差异686
正在比较文件 py.out 和 GOOD.OUT
FC: 找不到差异995
正在比较文件 py.out 和 GOOD.OUT
FC: 找不到差异1597
正在比较文件 py.out 和 GOOD.OUT
FC: 找不到差异

倪文迪陪你学蓝桥杯2021寒假每日一题:1.19日(2018省赛A组第7题)相关推荐

  1. 倪文迪陪你学蓝桥杯2021寒假每日一题:1.20日(2018省赛A组第8题)

    2021年寒假每日一题,2017~2019年的省赛真题. 本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供. 后面的每日一题,每题发一个新博文,请大家每天看博客蓝桥杯专栏: htt ...

  2. 倪文迪陪你学蓝桥杯2021寒假每日一题:1.15日(2018省赛A组第3题)

    2021年寒假每日一题,2017~2019年的省赛真题. 本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供. 后面的每日一题,每题发一个新博文,请大家每天看博客蓝桥杯专栏: htt ...

  3. 倪文迪陪你学蓝桥杯2021寒假每日一题:1.21日(2018省赛A组第9题)

    2021年寒假每日一题,2017~2019年的省赛真题. 本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供. 后面的每日一题,每题发一个新博文,请大家每天看博客蓝桥杯专栏: htt ...

  4. 倪文迪陪你学蓝桥杯2021寒假每日一题:2.1日(2019省赛A组第10题)

    2021年寒假每日一题,2017~2019年的省赛真题.本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供.每日一题,关注蓝桥杯专栏: https://blog.csdn.net/w ...

  5. 倪文迪陪你学蓝桥杯2021寒假每日一题:1.26日(2019省赛A组第4题)

    2021年寒假每日一题,2017~2019年的省赛真题.本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供.每日一题,关注蓝桥杯专栏: https://blog.csdn.net/w ...

  6. 倪文迪陪你学蓝桥杯2021寒假每日一题:1.11日(2017省赛A第9题)

    2021年寒假每日一题,2017~2019年的省赛真题. 本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供. 后面的每日一题,每题发一个新博文,请大家看博客目录:https://b ...

  7. 第十二届 2021年1月 蓝桥杯青少年组省赛C++组 第1题--第3题(scratch实现)

    第十二届2021年蓝桥杯青少年组省赛 第十二届2021年蓝桥杯青少年组省赛_lybc2019的博客-CSDN博客 第十二届蓝桥杯青少年组省赛C++中级组试卷讲解(2021.01) 第十二届蓝桥杯青少年 ...

  8. 第十二届蓝桥杯 2021年国赛真题 (Java 大学A组)

    蓝桥杯 2021年国赛真题(Java 大学 A 组 ) #A 纯质数 按序枚举 按位枚举 #B 完全日期 朴素解法 朴素改进 #C 最小权值 动态规划 #D 覆盖 变种八皇后 状压 DP #E 123 ...

  9. 第十二届蓝桥杯 2021年省赛真题 (Java 大学B组) 第一场

    蓝桥杯 2021年省赛真题 (Java 大学B组 ) #A ASC #B 卡片 朴素解法 弯道超车 #C 直线 直线方程集合 分式消除误差 平面几何 #D 货物摆放 暴力搜索 缩放质因子 #E 路径 ...

最新文章

  1. Rosetta | Rosetta简介
  2. 建立一个按年龄排序的有序链表,每个结点包括学号、姓名、性别、年龄。建立一个新的结点,通过年龄将此结点插入到链表中去,使之仍然有序
  3. flutter 自定义tab导航-顶部导航-底部导航
  4. MyBatis之PageHelper分页操作
  5. android 复制文件到指定文件夹_按指定名称批量新建文件夹,其实并不难
  6. c语言long的格式字符串,时间字符串和long类型之间的转换
  7. ApacheCN Java 译文集 20211012 更新
  8. 11.17 dfs poj1979 Red and Black
  9. sessionstorage,localstorage和cookie
  10. 千兆路由器什么牌子好?家用千兆路由器2018排行!
  11. Ubuntu校园iNode客户端超简单安装
  12. 欧氏空间距离和内积_希尔伯特空间(Hilbert Space)
  13. Fall 2020 Berkeley cs61a hw03答案
  14. Springboot项目调用阿里云语音服务案例【真实有用】
  15. Spring Boot实战分页查询附近的人: Redis+GeoHash+Lua
  16. html5数学公式编辑器,Daum Equation Editor:数学公式编辑器
  17. WIN10 如何隐藏桌面图标
  18. 针对RK3328平台搭建支持KVM的Linux环境
  19. 关于Hibernate的Session和SessionFactory
  20. twctf_2018_bbq

热门文章

  1. Agv、Rgv 车辆控制调度系统开发第八篇-错误纠正
  2. Linux-OpenLDAP服务集中管理用户账户信息
  3. 贡献黑莓SDK for Eclipse 工具
  4. wce实现hash注入
  5. 脚本提取大华DAV文件
  6. PU learning
  7. Dis-PU复现踩坑
  8. 一周 Go World 新鲜事-2018W54
  9. 精致的像素级别的风格转换 ----- Deep Image Analogy
  10. 同时查询多个快递单号物流单号查询