51Nod 加农炮

一个长度为M的正整数数组A,表示从左向右的地形高度。测试一种加农炮,炮弹平行于地面从左向右飞行,高度为H,如果某处地形的高度大于等于炮弹飞行的高度H(A[i] >= H),炮弹会被挡住并落在i - 1处,则A[i - 1] + 1。如果H <= A[0],则这个炮弹无效,如果H > 所有的A[i],这个炮弹也无效。现在给定N个整数的数组B代表炮弹高度,计算出最后地形的样子。
例如:地形高度A = {1, 2, 0, 4, 3, 2, 1, 5, 7}, 炮弹高度B = {2, 8, 0, 7, 6, 5, 3, 4, 5, 6, 5},最终得到的地形高度为:{2, 2, 2, 4, 3, 3, 5, 6, 7}。

Input

第1行:2个数M, N中间用空格分隔,分别为数组A和B的长度(1 <= m, n <= 50000)
第2至M + 1行:每行1个数,表示对应的地形高度(0 <= A[i] <= 1000000)。
第M + 2至N + M + 1行,每行1个数,表示炮弹的高度(0 <= B[i] <= 1000000)。

Output

输出共M行,每行一个数,对应最终的地形高度。

Input示例
9 11
1
2
0
4
3
2
1
5
7
2
8
0
7
6
5
3
4
5
6
5
Output示例
2
2
2
4
3
3
5
6
7
Java的运行时限为:3000 ms ,空间限制为:262144 KB 
这一题的意思就是炮弹被挡住后,落在前面的地上,前面的地形高度就+1了。。。好吧。。。

我的思路就是轮询,报着TLE的心态提交后,居然AC了。。。

package leetcode;

import java.io.PrintWriter;
import java.util.Scanner;

public class Node51_8_Cannon{
	
	public int[] solve(int m,int n,int[] A,int[] B){
		int groundPointer=0;
		for(int cannonballPointer=0;cannonballPointer<n;cannonballPointer++){
			for(groundPointer=0;groundPointer<m;groundPointer++){
				if(A[groundPointer]>=B[cannonballPointer]){
					break;
				}
			}
			if(groundPointer==0||groundPointer==m){
				continue;
			}
			else{
				A[groundPointer-1]++;
			}
		}
		return A;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		PrintWriter out = new PrintWriter(System.out);

		int m = in.nextInt();//地形个数
		int n = in.nextInt();//炮弹个数
		int[] A=new int[m];//从左到右的地形高度
		int[] B=new int[n];//炮弹高度
		for(int i=0;i<m;i++){
			A[i]=in.nextInt();
		}
		for(int i=0;i<n;i++){
			B[i]=in.nextInt();

		}
		in.close();
		Node51_8_Cannon node = new Node51_8_Cannon();
		int[] result = node.solve(m, n, A,B);
		for(int i=0;i<m;i++){
			out.println(result[i]);
		}	
		out.flush();
	}

}
大神说这道题可以用线段树。。什么叫线段树我搜了一下,然后放在了下一篇博文里。

这道题我们知道我们要做的是查询加修改,找到每一个加农炮能到达的位置,然后修改前一个,一开始我们知道山的高度是具有单调性的,这样我们预处理一遍高度,使其单调递增,这样我们可以利用二分很快找到加农炮落到的位置,查询的问题解决了,但是修改呢,我们没一次修改后,他的单调性都会改变,如果我们再去处理一遍单调性,那么时间复杂度最差的情况是O(n2),是不可以接受的,那么给怎样去修改呢,分析到这里我们其实已经能想到线段树了,线段树的好处就是对一段区间或者单点的更新和查询都是logn的复杂度,那么我们刚好需要这种数据结构,接下来就是分析怎么构建出这颗线段树了,首先是查询,怎么去查询呢,我们可以想到储存每个区间的高度的最大值,然后根据左子树的最大值和右子树的最大值判断是去左子树还是右子树,对于左子树注意是>=,因为保证优先去左边,知道查到叶子节点,然后去修改,修改的话就容易多了,更新查询返回位置-1的位置就好了

#include<cstdio>  
#include<iostream>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
struct node{  
    int l,r,shu;  
}pp[200000];  
int p[50100];  
void query(int x)  
{  
    pp[x].shu=max(pp[x*2].shu,pp[x*2+1].shu);  
}  
void create(int l,int r,int x)  
{  
    pp[x].l=l;pp[x].r=r;  
    if (l==r)  
    {  
        pp[x].shu=p[l];return;  
    }  
    int m=(l+r)>>1;  
    create(l,m,x*2);  
    create(m+1,r,x*2+1);  
    query(x);  
}  
int search_max(int x,int a)  
{  
    if (pp[x].l==pp[x].r) return pp[x].l;  
    if (pp[x*2].shu>=a) return search_max(x*2,a);  
    return search_max(x*2+1,a);  
}  
void update(int x,int a)  
{  
    if (pp[x].l==pp[x].r)  
    {  
        pp[x].shu=p[a];return ;  
    }  
    if (a<=(pp[x].l+pp[x].r)>>1)  
        update(x*2,a);  
    else  
        update(x*2+1,a);  
    query(x);  
}  
int main()  
{  
    int n,m,a;  
    cin>>m>>n;  
    for (int i=1;i<=m;i++)  
        cin>>p[i];  
    create(1,m,1);  
    for (int i=0;i<n;i++)  
    {  
        cin>>a;  
        if (a<=p[1]||a>pp[1].shu)  
            continue;  
        int k=search_max(1,a);  
        p[k-1]++;  
        update(1,k-1);  
    }  
    for (int i=1;i<=m;i++)  
        cout<<p[i]<<endl;  
    return 0;  
}  

我自己也试着用这个思路完成了一个Java版的。需要注意的是SegmentTreeNode需要比较大,所以这里用了200000,因为树有n层,那么就需要数组有2^n大,这个增长速度很快的。


package leetcode;

import java.io.PrintWriter;
import java.util.Scanner;

public class Node51_8_Cannon_SegmentTree {

	public int[] solve(int m,int n,int[] A,int[] B){
		SegmentTreeNode[] tree=new SegmentTreeNode[200000];//index为0空着
		createTree(tree, A, 1, 1, m);
		for(int i=0;i<n;i++){
			int paodan=B[i];
			int mostHigh=tree[1].val;
			if(paodan<=A[1]||paodan>mostHigh){//炮弹被地形1挡下来,或者炮弹飞过全部地形,continue
				continue;
			}
			else{
				int updateIndex=queryTree(tree, 1, paodan)-1;//从1开始往下搜索,-1是因为要得到前一个地形
				A[updateIndex]++;
				updateTree(tree, A, 1, updateIndex);//从1开始往下搜索
			}
		}
		return A;
	}
	
	public void createTree(SegmentTreeNode[] tree,int[] A,int index,int left,int right){
		tree[index]=new SegmentTreeNode(left, right);
		if(left==right){
			tree[index].val=A[left];
			return;
		}
		int middle=(left+right)/2;
		createTree(tree, A, index*2, left, middle);
		createTree(tree, A, index*2+1, middle+1, right);
		tree[index].val=Math.max(tree[index*2].val, tree[index*2+1].val);
	}
	
	public int queryTree(SegmentTreeNode[] tree,int index,int paodan){//找到第一个大于等于paodan的数在A中的index
		if(tree[index].left==tree[index].right){
			return tree[index].left;
		}
		else{
			if(tree[index*2].val>=paodan){
				return queryTree(tree, index*2, paodan);//因为炮弹从左边飞来,所以当左子树大于时,优先考虑左子树
			}
			else{
				return queryTree(tree, index*2+1, paodan);
			}
		}
	}
	
	public void updateTree(SegmentTreeNode[] tree,int[] A,int index,int APointer){
		if(tree[index].left==tree[index].right){
			tree[index].val=A[APointer];
			return;
		}
		int middle=(tree[index].left+tree[index].right)/2;
		if(middle>=APointer){//左子树的范围包括APointer
			updateTree(tree, A, index*2, APointer);
		}
		else{
			updateTree(tree, A, index*2+1, APointer);
		}
		tree[index].val=Math.max(tree[index*2].val, tree[index*2+1].val);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		PrintWriter out = new PrintWriter(System.out);

		int m = in.nextInt();//地形个数
		int n = in.nextInt();//炮弹个数
		int[] A=new int[m+1];//从左到右的地形高度,index为0空着
		int[] B=new int[n];//炮弹高度
		for(int i=1;i<=m;i++){
			A[i]=in.nextInt();
		}
		for(int i=0;i<n;i++){
			B[i]=in.nextInt();

		}
		in.close();
		Node51_8_Cannon_SegmentTree node = new Node51_8_Cannon_SegmentTree();
		int[] result = node.solve(m, n, A,B);
		for(int i=1;i<=m;i++){
			out.println(result[i]);
		}	
		out.flush();
	}
	
	class SegmentTreeNode{
		int left;//这个线段点的左边界限
		int right;//这个线段点的右边界限
		int val;
		public SegmentTreeNode(int left,int right){
			this.left=left;
			this.right=right;
		}
		public SegmentTreeNode(int val,int left,int right){
			this.val=val;
			this.left=left;
			this.right=right;
		}
	}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值