算法训练 操作格子

问题描述

有n个格子,从左到右放成一排,编号为1-n。

共有m次操作,有3种操作类型:

1.修改一个格子的权值,

2.求连续一段格子权值和,

3.求连续一段格子的最大值。

对于每个2、3操作输出你所求出的结果。

输入格式

第一行2个整数n,m。

接下来一行n个整数表示n个格子的初始权值。

接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

输出格式

有若干行,行数等于p=2或3的操作总数。

每行1个整数,对应了每个p=2或3操作的结果。

样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。

----------------------------------------------------------------------------------------

这道题看起来挺容易,就是操作数组,所以一开始我只是用简简单单的数组操作做这道题,结果运行超时;最后我用了树状数组来做这道题,也是超时。查了别人用同样的算法却不会超时,郁闷。

先附上树状数组的解法

import java.util.Scanner;

public class Main {
	static Scanner reader = new Scanner(System.in);
	static treePoint[] tree = null;
	static int n;
	static int m;
	static int[] subPoint;
	static int step = 0;

	public static void main(String[] args) {

		n = reader.nextInt();
		m = reader.nextInt();
		tree = new treePoint[(n*4) ];//树状数组这里要定义结点的四倍
		subPoint = new int[n];//保存下标
		int[][] operate = new int[m][3];//保存输入的操作
		build(0, 0, n - 1);//建立树状树
		int max;
		int sum;
		for (int i = 0; i < n; i++) {
			int temp = reader.nextInt();
			tree[subPoint[i]].max = temp;
			tree[subPoint[i]].sum = temp;
		}
		//更新节点信息,主要是因为叶子节点数值修改了,那么其祖辈也需要更改
		for (int i = 0; i < n; i++) {
			upPoint(subPoint[i]);
		}
		//读取操作
		for (int i = 0; i < m; i++) {
			operate[i][0] = reader.nextInt();
			operate[i][1] = reader.nextInt();
			operate[i][2] = reader.nextInt();
		}
		//对操作进行判断输出
		for (int i = 0; i < m; i++) {
			//更改叶子节点信息
			if (operate[i][0] == 1) {
				tree[subPoint[operate[i][1] - 1]].sum = operate[i][2];
				tree[subPoint[operate[i][1] - 1]].max = operate[i][2];
				upPoint(subPoint[operate[i][1] - 1]);//更新祖辈
			}
			//得到区间的和
			else if (operate[i][0] == 2) {
			
				sum = getSum(0, operate[i][1] - 1, operate[i][2] - 1);
				System.out.println(sum);
			}
			//得到区间的最大值
			else if (operate[i][0] == 3) {
				max = getMax(0, operate[i][1] - 1, operate[i][2] - 1);
				System.out.println(max);
			}

		}
	}
	//求区间和的函数
	public static int getSum(int i, int left, int right) {
		//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]中,那么返回其区间的和,不需要在向下搜索
		if (tree[i].left >= left && tree[i].right <= right) {
			return tree[i].sum;
		}
		//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]的右区间,那么在右区间向下搜索
		else if ((tree[i].left + tree[i].right) / 2 < left) {
			return getSum((i << 1) + 2, left, right);
		}
		//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]的右区间,那么在左区间向下搜索
		else if ((tree[i].left + tree[i].right) / 2 >= right) {
			return getSum((i << 1) + 1, left, right);
		}
		//如果以上条件不符合,那么就落在区间中间
		else {
			return getSum((i << 1) + 2, left, right) + getSum((i << 1) + 1, left, right);
		}
			
	}
	//求区间的最大值,与求区间和的原理类似
	public static int getMax(int i, int left, int right) {
		if (tree[i].left >= left && tree[i].right <= right) {
			return tree[i].max;
		} else if ((tree[i].left + tree[i].right) / 2 < left) {
			return getMax((i << 1) + 2, left, right);
		} else if ((tree[i].left + tree[i].right) / 2 >= right) {
			return getMax((i << 1) + 1, left, right);
		} else
			return Math.max(getMax((i << 1) + 2, left, right), getMax((i << 1) + 1, left, right));

	}
	//更新祖辈信息
	public static void upPoint(int i) {
		//不是叶子节点就更新
		if (tree[i].left != tree[i].right) {
			tree[i].max = Math.max(tree[(i << 1) + 1].max, tree[(i << 1) + 2].max);
			tree[i].sum = tree[(i << 1) + 1].sum + tree[(i << 1) + 2].sum;
		}
		//到根节点结束
		if (i == 0)
			return;
		//向父亲节点更新,递归操作
		upPoint((i - 1) / 2);
	}
	//建树
	public static void build(int i, int left, int right) {

		tree[i] = new treePoint();
		tree[i].left = left;
		tree[i].right = right;
		if (left == right) {
			subPoint[step++] = i;//保存叶子节点的信息
			return;
		} else {	
			build((i << 1) + 1, left, (left + right) / 2);//建立左区间
			build((i << 1) + 2, (left + right) / 2 + 1, right);//建立右区间

		}
	}
}

class treePoint {
	int left = 0;
	int right = 0;
	int max;
	int sum;
}

运行结果是30分,下面我附上简单的数组操作的写法
import java.util.Scanner;

public class Main{
	public static void main(String[] args)
	{
		Scanner reader=new Scanner(System.in);
		int n=reader.nextInt();
		int m=reader.nextInt();
		int[] arr=new int[n];
		int[][] open=new int[m][3];
		for(int i=0;i<n;i++)
		{
			arr[i]=reader.nextInt();
		}
		for(int i=0;i<m;i++)
		{
			open[i][0]=reader.nextInt();
			open[i][1]=reader.nextInt();
			open[i][2]=reader.nextInt();
		}
		for(int i=0;i<m;i++)
		{
			if(open[i][0]==1)
			{
				arr[open[i][1]-1]=open[i][2];
			}
			else if(open[i][0]==2)
			{
				int result=0;
				for(int s=open[i][1]-1;s<open[i][2];s++)
				{
					result+=arr[s];
				}
				System.out.println(result);
			}
			else if(open[i][0]==3)
			{
				int max=Integer.MIN_VALUE;
				for(int s=open[i][1]-1;s<open[i][2];s++)
				{
					if(arr[s]>max)
						max=arr[s];
				}
				System.out.println(max);
			}
		}
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值