2018第九届蓝桥杯个人赛省赛(软件类)C++A组试题第七题

一【题目描述】


标题:三体攻击

【题目描述】
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 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 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。

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

【输入格式】
从标准输入读入数据。

第一行包括 4 个正整数 A, B, C, m;
第二行包含 A × B × C 个整数,其中第 ((i − 1)×B + (j − 1)) × C + (k − 1)+1 个数为 d(i, j, k);
第 3 到第 m + 2 行中,第 (t − 2) 行包含 7 个正整数 lat, rat, lbt, rbt, lct, rct, ht。

【输出格式】
输出到标准输出。

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

【样例输入】
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

【样例解释】
在第 2 轮攻击后,战舰 (1,1,1) 总共受到了 2 点伤害,超出其防御力导致爆炸。

【数据约定】
对于 10% 的数据,B = C = 1;
对于 20% 的数据,C = 1;
对于 40% 的数据,A × B × C, m ≤ 10, 000;
对于 70% 的数据,A, B, C ≤ 200;
对于所有数据,A × B × C ≤ 10^6, m ≤ 10^6, 0 ≤ d(i, j, k), ht ≤ 10^9。


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。

提交程序时,注意选择所期望的语言类型和编译器类型。
 

二【解题思路】

     我们把这个题目大致的思路给出来,如果你看过关于星际大战的动漫或者电影,应该能够理解这个立方体战舰的排布。我们需要做的就是每轮攻击都是,对被攻击的战舰都进行减少血量操作,然后扫描判断,如果收到的攻击超过其防御力,那么就会爆炸,然后输出本轮攻击轮数即可,那么我们的逻辑就有如下的代码,其实主要就是数组的操作。

#include<iostream>
using namespace std;

int A,B,C,m,a,b,c;

int getInt()//因为读取的数据可能会到达10^6级别,所以我们采用快速读取的方法
{
	char ch = getchar();//就是累积读取之后乘10再相加就可以
	int x=0,f = 1;
	while(ch<'0'||ch>'9'){//出现负数的情况,当然这个你可以省略
		if(ch='-') f=-1;
		ch = getchar();
	}
	while('0'<=ch&&ch<='9'){
		x = x*10+ch -'0';
		ch = getchar();
	}
	return x*f;//最后就得到了输入的A,B,C,m
}

inline int getIndex(int i,int j,int k){//我们这里定义为内联函数,可以降低时间的复杂度
	return ((i-1)*b+(j-1))*c+k;//直接返回的就是这个位置,因为我们是一维的数组
}

int main()
{
	A = getInt();
	B = getInt();
	C = getInt();
	m = getInt();
	a = A+1;b = B+1;c = C+1;//我们下标从1开始,所以数组的长度都加个1
	
	int *data = new int[a*b*c];
	int (*atk)[7] = new int[m+1][7];//m轮攻击
	
	for(int i=1;i<=A;i++)
	{
		for(int j=1;j<=B;j++)
		{
			for(int k = 1;k<=C;k++)
			{
				data[getIndex(i,j,k)] = getInt();//初始化血量
			}
		}
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<7;j++)
		{
			atk[i][j] = getInt();//攻击数组
		}
		//执行攻击
		for(int x=atk[i][0];x<=atk[i][1];x++)//层的范围 
		{
			for(int y = atk[i][2];y<=atk[i][3];y++)//行的范围
			{
				for(int z = atk[i][4];z<=atk[i][5];z++)//列的范围
				{
					data[getIndex(x,y,z)]-=atk[i][6];//减少血量
					if(data[getIndex(x,y,z)]<0){//进行判断
						cout<<i<<endl;
						delete[]data;//释放内存
						delete[]atk;
						return 0;
					}
				 } 
			 } 
		 } 
	 } 
	delete[]data;
	delete[]atk;
	return 0;
		
}	

     上面的程序是没有问题的,但是我们稍微计算一下,看向四层的for循环,最大的时候A*B*C = 10^6,m = 10^6,那么时间复杂度就为O(10^12),显然两秒是跑不出来了,所以我们需要考虑优化。那么对多层循环的优化一般有:减少枚举层数,减少枚举范围,二分法。前面两种估计不怎么可能,所以我们考虑对m进行二分来优化数据。那么这里就用到了前缀和,差分。

前缀和:O(N)维护数组,O(1)求区间和。差分:O(N)求单点最终值,O(1)改变区间值。利用这两个却进行优化,如果你想了解更多的差分知识,这个博主讲解的很清晰,https://www.cnblogs.com/scx2015noip-as-php/p/2018_10_18.html那么开始码代码吧。

三【解题步骤】

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i=(x);i<=(y);i++)
#define dwn(i,x,y) for(register int i=(x);i>=(y);i--)
#define maxn 1000010
#define LL long long
using namespace std;
int read()//和上面一样的快速读取
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*f;
}

void write(int x)
{
    int f=0;char ch[20];
    if(!x){putchar('0'),putchar('\n');return;}
    if(x<0)x=-x,putchar('-');
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    putchar('\n');
}

LL qh[maxn],a[maxn],s[maxn],d[maxn];
int qxa[maxn],qya[maxn],qza[maxn],qxb[maxn],qyb[maxn],qzb[maxn],A,B,C,m,ans;
int getx(int x,int y,int z){return (x*B+y)*C+z+1;}
void getp(int x,int px,int py,int pz){x--;pz=x%C,x/=C,py=x%B,pz=x/B;return;}
void add(int x,int y,int z,LL h){if(x<=(A-1)&&y<=(B-1)&&z<=(C-1))a[getx(x,y,z)]+=h;}
void atk(int xa,int ya,int za,int xb,int yb,int zb,LL h)
{
    add(xa,ya,za,h),add(xa,ya,zb+1,-h),add(xa,yb+1,za,-h),add(xa,yb+1,zb+1,h);
    add(xb+1,ya,za,-h),add(xb+1,ya,zb+1,h),add(xb+1,yb+1,za,h),add(xb+1,yb+1,zb+1,-h);
}

int check()
{
    rep(i,0,A-1)rep(j,0,B-1)rep(k,0,C-1)s[getx(i,j,k)]=a[getx(i,j,k)];
    if(C!=1)rep(i,0,A-1)rep(j,0,B-1)rep(k,1,C-1)s[getx(i,j,k)]+=s[getx(i,j,k-1)];//,cout<<i<<" "<<j<<" "<<k<<"+1 "<<s[getx(i,j,k-1)]<<endl;
    if(B!=1)rep(i,0,A-1)rep(j,1,B-1)rep(k,0,C-1)s[getx(i,j,k)]+=s[getx(i,j-1,k)];//,cout<<i<<" "<<j<<" "<<k<<"+2 "<<s[getx(i,j-1,k)]<<endl;
    if(A!=1)rep(i,1,A-1)rep(j,0,B-1)rep(k,0,C-1)s[getx(i,j,k)]+=s[getx(i-1,j,k)];//,cout<<<<"+3 "<<s[getx(i-1,j,k)]<<endl;
    rep(i,0,A-1)rep(j,0,B-1)rep(k,0,C-1)if(s[getx(i,j,k)]>d[getx(i,j,k)])return 1;
    return 0;
}

void getans(int L,int R,int lastmid)
{
    if(L>R)return;
    int mid=(L+R)>>1;
    if(mid>lastmid)rep(i,lastmid+1,mid)/*cout<<"mid:"<<mid<<" add:"<<i<<endl,*/atk(qxa[i],qya[i],qza[i],qxb[i],qyb[i],qzb[i],qh[i]);
    if(mid<lastmid)rep(i,mid+1,lastmid)/*cout<<"mid:"<<mid<<" del:"<<i<<endl,*/atk(qxa[i],qya[i],qza[i],qxb[i],qyb[i],qzb[i],-qh[i]);
    int f=check();

    //rep(i,0,A-1)rep(j,0,B-1)rep(k,0,C-1)cout<<s[getx(i,j,k)]<<" ";cout<<endl;
    if(f){ans=min(ans,mid);getans(L,mid-1,mid);}
    else getans(mid+1,R,mid);
}

int main()
{

    A=read(),B=read(),C=read(),ans=m=read();
    rep(i,0,A-1)rep(j,0,B-1)rep(k,0,C-1)d[getx(i,j,k)]=read();
    rep(i,1,m)qxa[i]=read()-1,qxb[i]=read()-1,qya[i]=read()-1,qyb[i]=read()-1,qza[i]=read()-1,qzb[i]=read()-1,qh[i]=read();
    getans(1,m,0);
    write(ans);
    return 0;
}

 

四【总结】

     我们在看到题目进行分析的时候,如果是数组其实可以画图理解,然后进行枚举,再考虑优化。如有更好的解法,欢迎交流哦。

猜你喜欢

转载自blog.csdn.net/qq_43919400/article/details/105838531