【蓝桥杯】三体攻击

三体攻击(不知道为什么练习系统会超时,但是在这里提交就可以)

三体攻击

三体人将对地球发起攻击。为了抵御攻击,地球人派出 A   ×   B   ×   C A \times B \times 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 lat, rat, lbt, rbt, lct, rct, ht 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 ∈ [lat, rat],j ∈ [lbt, rbt],k ∈ [lct, rct] i[lat,rat],j[lbt,rbt],k[lct,rct] 的战舰 ( i ,   j ,   k ) (i, j, k) (i,j,k) 会受到 ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。

地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。

输入描述
输入格式:

第一行包括 4 个正整数 A, B, C, m;

第二行包含 A   ×   B   ×   C A \times B \times C A×B×C 个整数,其中第 ( ( i   −   1 ) × B   +   ( j   −   1 ) )   ×   C   +   ( k   −   1 ) + 1 ((i − 1)\times B + (j − 1)) \times C + (k − 1)+1 ((i1)×B+(j1))×C+(k1)+1 个数为 d ( i ,   j ,   k ) d(i, j, k) d(i,j,k)

第 3 到第 m + 2 行中,第 (t − 2) 行包含 7 个正整数 l a t ,   r a t ,   l b t ,   r b t ,   l c t ,   r c t ,   h t lat, rat, lbt, rbt, lct, rct, ht lat,rat,lbt,rbt,lct,rct,ht

其中, A   ×   B   ×   C   ≤   1 0 6 ,   m   ≤   1 0 6 ,   0   ≤   d ( i ,   j ,   k ) ,   h t   ≤   1 0 9 A \times B \times C  \leq  10^6, m  \leq  10^6, 0  \leq  d(i, j, k), ht  \leq  10^9 A×B×C106,m106,0d(i,j,k),ht109

输出描述
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。

输入输出样例

输入

2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2

输出

2
思路
  • 暴力+二分+三维差分优化
  • 由于是求第几轮第一艘战舰爆炸,可知,在第一艘战舰爆炸之前没有战舰爆炸,之后肯定有战舰爆炸,符合“单调性”,可以用二分
  • 整体框架是二分,关键就在于二分的check()函数怎么写了,若是单纯地暴力,那么会超时,这里使用差分数组
  • 关于差分数组的知识这里省略,其实可以手动模拟出来,只是有些费时,三维的还会看花眼
  • 得出三维的差分数组求法:
//三维差分数组求法
//a是原数组,是p层n行m列的三维数组
    for (int i = 1; i <= p; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= m; k++) {
                b[i][j][k] = a[i][j][k] - a[i - 1][j][k] - a[i][j][k - 1] +
                             a[i - 1][j][k - 1] - a[i][j - 1][k] +
                             a[i - 1][j - 1][k] + a[i][j - 1][k - 1] -
                             a[i - 1][j - 1][k - 1];
            }
        }
    }
//给点(x1,y1,z1)到点(x2,y2,z2)之间的三维区间都加上d
//b是差分数组
void change_matrix(int x1, int y1, int z1, int x2, int y2, int z2, int d) {
    b[z1][x1][y1] += d;
    b[z1][x1][y2 + 1] -= d;
    b[z1][x2 + 1][y1] -= d;
    b[z1][x2 + 1][y2 + 1] += d;
    b[z2 + 1][x1][y2 + 1] += d;
    b[z2 + 1][x2 + 1][y1] += d;
    b[z2 + 1][x1][y1] -= d;
    b[z2 + 1][x2 + 1][y2 + 1] -= d;
}
  • 不过以上都是三维形式的数组,这道题还要坑一些,用三维数组有点麻烦,要使用动态开辟空间,这里就将它转为一维数组,转换方法题目已经给了,写成一个函数即可
int id(int i, int j, int k) {
    if (i > A || j > B || k > C) return 0;
    return ((i - 1) * B + (j - 1)) * C + (k - 1) + 1;
}
  • 有了这些之后,就可以写出check()函数了,check()函数的思路是:对于某一轮攻击 t t t,如果从第一轮攻击到第 t t t 轮攻击使得有战舰爆炸,那么就返回真,否则返回假
代码如下
#include <cstdio>
#include <vector>
using namespace std;

vector<int> d;            //存原始数据
vector<vector<int> > dk;  //存攻击
int A, B, C, n, m;        // A层B行C列m轮,n = A*B*C

//下标转换
int id(int i, int j, int k) {
    if (i > A || j > B || k > C) return 0;
    return ((i - 1) * B + (j - 1)) * C + (k - 1) + 1;
}

// check()函数
bool check(int t) {
    int i = 0, j = 0, k = 0;
    vector<int> a;
    a.resize(n + 1, 0);
    for (i = 1; i <= t; i++) {
    	//全部转换为一维下标
        a[id(dk[i - 1][0], dk[i - 1][2], dk[i - 1][4])] += dk[i - 1][6];
        a[id(dk[i - 1][1] + 1, dk[i - 1][2], dk[i - 1][4])] -= dk[i - 1][6];
        a[id(dk[i - 1][0], dk[i - 1][3] + 1, dk[i - 1][4])] -= dk[i - 1][6];
        a[id(dk[i - 1][0], dk[i - 1][2], dk[i - 1][5] + 1)] -= dk[i - 1][6];
        a[id(dk[i - 1][1] + 1, dk[i - 1][3] + 1, dk[i - 1][4])] += dk[i - 1][6];
        a[id(dk[i - 1][0], dk[i - 1][3] + 1, dk[i - 1][5] + 1)] += dk[i - 1][6];
        a[id(dk[i - 1][1] + 1, dk[i - 1][2], dk[i - 1][5] + 1)] += dk[i - 1][6];
        a[id(dk[i - 1][1] + 1, dk[i - 1][3] + 1, dk[i - 1][5] + 1)] -=
            dk[i - 1][6];
    }

    //这三个三重循环是求前缀和
    for (i = 1; i <= A; i++) {
        for (j = 1; j <= B; j++) {
            for (k = 1; k < C; k++) {
                a[id(i, j, k + 1)] += a[id(i, j, k)];
            }
        }
    }
    for (i = 1; i <= A; i++) {
        for (k = 1; k <= C; k++) {
            for (j = 1; j < B; j++) {
                a[id(i, j + 1, k)] += a[id(i, j, k)];
            }
        }
    }
    for (j = 1; j <= B; j++) {
        for (k = 1; k <= C; k++) {
            for (i = 1; i < A; i++) {
                a[id(i + 1, j, k)] += a[id(i, j, k)];
            }
        }
    }

    for (i = 1; i <= n; i++) {
        if (a[i] > d[i - 1]) {
            return true;
        }
    }
    return false;
}

//读入数据
void init() {
    scanf("%d%d%d%d", &A, &B, &C, &m);
    n = A * B * C;
    int i = 0, j = 0;
    d.resize(n);
    for (i = 0; i < n; i++) {
        scanf("%d", &d[i]);
    }
    dk.resize(m);
    for (i = 0; i < m; i++) {
        dk[i].resize(7);
        for (j = 0; j < 7; j++) {
            scanf("%d", &dk[i][j]);
        }
    }
}

//二分
void solve() {
    int l = 1, r = m, mid = 0, ans = 0;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }

    printf("%d\n", ans);
}

int main(void) {
    init();
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渴望力量的猴子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值