Description:三体攻击
三体人将对地球发起攻击。
为了抵御攻击,地球人派出了 A × B × C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。其中,第i 层第 j 行第 k 列的战舰(记为战舰 (i, j, k))的生命值为 d(i, j, k)。三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。
具体地,第 t 轮攻击用 7 个参数lat, rat, lbt, rbt, lct, rct, ht 描述;所有满足
i ∈ [lat, rat],j ∈ [lbt, rbt],k ∈ [lct, rct] 的战舰 (i, j, k) 会受到 ht的伤害。
如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。
地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
思路1:暴力求解
暴力求解的方法是依次进行每一轮攻击,然后更新攻击范围内的战舰的生命值,当第t轮某一个战舰的血量小于0,那么直接返回 t.
思路2:二分+差分+前缀和
利用二分,第一次连续攻击m/2轮,然后判断战舰的生命值,如果此时没有战舰爆炸,那么再往后攻击m/4轮;如果此时有战舰爆炸,那么往前恢复m/4轮。不断二分,直到找到是在第几轮攻击时导致战舰爆炸。
那么怎么实现连续攻击m/2轮?
利用差分数组,再求前缀和
一、前缀和的概念:
例如,给出一组数 arr[ 1 , 2 , 3 , 4 , 5 ]
其前缀和 Sn[ 1, 3 , 6 , 10 , 15 ]
即 Sn[i] = Sn[i-1] + arr[i];
二、前缀和的特点:
- O(N)的时间维护
例如,arr[2] += 3;
则Sn[ 1, 3 , 6+3 , 10+3 , 15+3 ] - O(1)的时间获取区间和
例如,求在 [2,4] 的区间内数值和
即 Sn[4] - Sn[1] = 15 - 3 = 12
三、一维差分数组+前缀和:
四、二维差分数组+前缀和:
五、三维差分数组+前缀和:
1、三维差分数组
给定三个角的坐标la , ra , lb , rb , lc , rc和攻击力h
正面四个
arr[lc][lb][la] += h;
arr[rc+1][lb][la] -= h;
arr[lc][lb][ra+1] -= h;
arr[rc+1][lb][ra+1] += h;
左面补两个
arr[lc][rb+1][la] -= h;
arr[lc][rb+1][ra+1] += h;
上面再补两个
arr[rc+1][rb+1][la] += h;
arr[rc+1][rb+1][ra+1] -= h;
2、三维前缀和
画的很乱,建议各位自己试着画一画。
arr[i][j][k] += arr[i][j-1][k]; //后方
arr[i][j][k] += arr[i-1][j][k];//左侧
arr[i][j][k] += arr[i][j][k-1]; //下方
arr[i][j][k] -= arr[i-1][j-1][k]; //左后
arr[i][j][k] -= arr[i-1][j][k-1]; //左下
arr[i][j][k] -= arr[i][j-1][k-1]; //后下
arr[i][j][k] += arr[i-1][j-1][k-1]; //左后下
六、算法实现
- 暴力求解
private static int[] arr;
private static int[][] attacks;
private static int A,B,C,m;
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
A = cin.nextInt();
B = cin.nextInt();
C = cin.nextInt();
m = cin.nextInt();
arr = new int[(A+1)*(B+1)*(C+1)];
attacks = new int[m+1][7];
//初始化战舰生命值
for (int i = 1;i < A+1;i++){
for (int j = 1;j < B+1;j++){
for (int k = 1;k < C+1;k++){
arr[getIndex(i,j,k)] = cin.nextInt();
}
}
}
//执行攻击
for (int k = 1; k < m+1; k++) {
for (int m = 0;m < 7;m++) //读入攻击信息。
attacks[k][m] = cin.nextInt();
for (int i = attac