洛谷题单--算法[2-1] 前缀和、差分与离散化

这篇具有很好参考价值的文章主要介绍了洛谷题单--算法[2-1] 前缀和、差分与离散化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

0.铺垫学习:p1115最大子段和--前缀和+贪心+DP

1.p1719最大加权矩形--前缀和+贪心+DP+矩阵压缩


0.铺垫学习:p1115最大子段和--前缀和+贪心+DP

原题链接:P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

原题:

题目描述

给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n。

第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai​。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 

7
2 -4 3 -1 2 -4 3

输出

4

说明/提示

样例 1 解释

选取 [3,5]子段 {3,−1,2}其和为 4。

数据规模与约定

  • 对于 40%40% 的数据,保证 n≤2×10。
  • 对于 100%100% 的数据,保证 1≤n≤2×10,−10≤ai​≤10。

解法一:(精简版)

思路:

用到了前缀和+贪心的方法。

前缀和:

        用sum记录前缀和,一路循环累加过去,当sum变成负数时,继续循环到下一个数的时候就不要加上前面是负数的sum了(因为加上一个负数反而会变小,所以还不如不加),sum置为0,再继续累加。

贪心:

        用maxx来记录最大子段和,每一轮循环都比较maxx和sum,用maxx保留最大值,用sum去循环累加。

代码:

#include<iostream>
using namespace std;
int sum,maxx,j;
int main()
{
	int n;
	cin>>n>>maxx;
	sum=maxx;
	while(--n)
	{
		cin>>j;
		sum=sum>0?sum:0;
		sum+=j;
		maxx=maxx>sum?maxx:sum;
	}
	cout<<maxx;
}

解法二:(原理版) 

思路:

用暴力直接枚举 l,r ,求所有区间的和然后取最大值当然也可以,但是这道题会超时。所以我们来思考优化的方法。

首先,来看看这道题的例子:

2 -4 3 -1 2 -4 3
ans:4

发现选择区间[3,-1,2]是最优解,这是怎么推出来的呢?

首先让sum=第一个数2,下一个数是 -4 ,-4加上sum(值为2)=-2>-4,所以如果区间包括 -4 则一定会包括 2 此时更新sum=2+(-4)=-2

接下来 3 ,如果 3 加上前面的数 2 和 -4 则 sum=1,1<3,所以如果区间包括 3 则一定不会包括前面的 2 和 -4 (否则反而sum会小于3,所以还不如不加),前面的区间[2,-4]舍去。更新sum=3,区间更新为[3]

接下来是 -1 ,-1加上前面的区间[3],sum=3+(-1)=2,2比 -1 大,所以如果区间包含 -1 则一定也会包含 3,更新sum=2,区间更新为[3,-1]

接下来是 2 ,2加上前面的区间[3,-1],sum=sum+2=2+2=4,4比 2 大,所以如果区间包含 2 则一定也会包含 3和 -1,更新sum=4,区间更新为[3,-1,2]

接下来是 -4 ,-4 加上前面的区间[3,-1,2],sum=sum+(-4)=4+(-4)=0,0比 -4 大,所以如果区间包含 -4 则一定也会包含 3, -1 和 2,更新sum=0,区间更新为[3,-1,2,-4]

最后是 3 ,3 加上前面的区间[3,-1,2,-4],sum=sum+3=0+3=3,3等于3,所以最后这个3可加课不加,该题选择加上去,因为题目要求是连续的子段,如果后面还有数字可以让答案变得更大的话如果不加上这个3,那么两个子段就连不起来了,所以无脑加上。更新sum=0,区间更新为[3,-1,2,-4,3]

在推导的同时,我们用maxx来保留最大值,发现最大值是4,对应的子段是[3,-1,2]

总结一下过程:

  • 第一个数是一个有效序列
  • 如果一个数加上更新后的有效序列的值比这个数大,则该数加入有效序列
  • 如果一个数加上更新后的有效序列的值比这个数小,则有效序列清空,再将该数加入有效序列
  • 在执行上述过程中实时更新(DP)有效序列的所有元素之和并保留最大值

 代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,sum,maxx;
	cin>>n>>maxx; 
	sum=maxx;// 把第一个数放入有效序列
	while(--n)
	{
		int a;
		cin>>a;
		sum=max(a,sum+a);
		maxx=max(sum,maxx);
	} 
	cout<<maxx;
}

1.p1719最大加权矩形--前缀和+贪心+DP+矩阵压缩

 原题链接:P1719 最大加权矩形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 原题:

题目描述

为了更好的备战 NOIP2013,电脑组的几个女孩子 LYQ,ZSC,ZHQ 认为,我们不光需要机房,我们还需要运动,于是就决定找校长申请一块电脑组的课余运动场地,听说她们都是电脑组的高手,校长没有马上答应他们,而是先给她们出了一道数学题,并且告诉她们:你们能获得的运动场地的面积就是你们能找到的这个最大的数字。

校长先给他们一个 n×n 矩阵。要求矩阵中最大加权矩形,即矩阵的每一个元素都有一权值,权值定义在整数集上。从中找一矩形,矩形大小无限制,是其中包含的所有元素的和最大 。矩阵的每个元素属于 [−127,127] ,例如

 0 –2 –7  0 
 9  2 –6  2
-4  1 –4  1 
-1  8  0 –2

在左下角:

9  2
-4  1
-1  8

和为 1515。

几个女孩子有点犯难了,于是就找到了电脑组精打细算的 HZH,TZY 小朋友帮忙计算,但是遗憾的是他们的答案都不一样,涉及土地的事情我们可不能含糊,你能帮忙计算出校长所给的矩形中加权和最大的矩形吗?

输入格式

第一行:n,接下来是 n 行 n 列的矩阵。

输出格式

最大矩形(子矩阵)的和。

输入输出样例

输入 

4
 0 -2 -7 0
 9 2 -6  2
-4 1 -4  1 
-1 8  0 -2

输出

15

说明/提示

1≤n≤120

 

思路:

总体思路其实就是第0题铺垫学习的内容再多加了一个压缩矩阵的操作。下面来讲解如何压缩矩阵,简单来说就是将二维的矩阵变成一维的,即数学里的化归的思想。

假设有一个矩阵:

9   2  -6  

-4  1  -4  

-1  8  0  

第一次我们取第一行:

9   2  -6  

其最大子序列和为 11(即9+2) ,max=11

第二次取第一、二行:

9   2  -6  

-4  1  -4  

注意!矩阵压缩的精髓来啦,我们将每一列的数字相加,将多行变为一行,即将二维变成一维了,这就是压缩。

第一列相加:9-4=5

第二列相加:2+1=3

第三列相加:-6-4=-10

所以压缩后的一维数组为:

5  3  -10

其最大子序列和为 8,max=11 不变

第三次取第一、二、三行:

9   2  -6  

-4  1  -4  

-1  8  0  

继续压缩,每列对应数字相加,将三维的压缩成一维

第一列相加:9-4-1=4

第二列相加:2+1+8=11

第三列相加:-6-4+0=-10

所以压缩后的一维数组为:

4  11  -10

其最大子序列和为 15,max=15

第四次取第二行:

-4  1  -4  

其最大子序列和为 1 ,max=15

第五次取第二、三行:

-4  1  -4  

-1  8  0  

第一列相加:-4-1=-5

第二列相加:1+8=9

第三列相加:-4+0=-4

 所以压缩后的一维数组为:

-5  9  -4

其最大子序列和为 9,max=15

第六次取第三行:

 -1  8  0  

其最大子序列和为 8,max=15

综上:

max=15,最大加权矩阵为 [(0,0),(2,1)]:

9   2  

-4  1    

-1  8  

代码: 

#include<bits/stdc++.h>
using namespace std;
int n;
int ans=1<<31; // int型数据的最小值 
int ma[125][125];
int temp[125],dp[125];

void sum()
{
	memset(dp,0,sizeof(dp));
	dp[0]=temp[0];
	for(int i=1;i<=n;i++)
	{
		dp[i]=max(dp[i],dp[i-1]+temp[i]); // 前缀和+贪心--与题p1115(最大子串和)同理 
		ans=max(ans,dp[i]); // DP 
	}
}

void arrsum()
{
	for(int i=0;i<n;i++) // 压缩矩阵 
	{
		memset(temp,0,sizeof(temp));
		for(int j=i;j<n;j++) // 遍历矩阵的行
		{
			for(int k=0;k<n;k++) // 遍历矩阵的列 
			{
				temp[k]+=ma[j][k];
			}
			sum();
		}
	}
}

int main() 
{
	cin>>n;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>ma[i][j];
	arrsum();
	cout<<ans; 
}

 ps:如果代码里的 memset() 函数不清楚的朋友可以阅读下面这位大佬的文章,这里就不赘述。

        memset的用法详解-CSDN博客文章来源地址https://www.toymoban.com/news/detail-835657.html

到了这里,关于洛谷题单--算法[2-1] 前缀和、差分与离散化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 洛谷题单 Part 8.2 最短路问题

    最短路算法一般在算法竞赛中有四种比较常见, F l o y d Floyd Fl oy d 算法, B e l l m a n − F o r d Bellman-Ford B e ll man − F or d 算法, D i j k s t r a Dijkstra D ijk s t r a 算法, S P F A SPFA SPF A 算法。 F l o y d Floyd Fl oy d 算法和 B e l l m a n − F o r d Bellman-Ford B e ll man − F or d 算法的时间复杂

    2024年02月09日
    浏览(53)
  • 【算法基础】前缀和与差分

    😽 PREFACE 🎁欢迎各位→点赞 👍 + 收藏 ⭐ + 评论 📝 📢系列专栏: 算法 💪 种一棵树最好是十年前其次是现在 前缀和指一个数组的某下标之前的所有数组元素的和(包含其自身)。前缀和分为一维前缀和,以及二维前缀和。前缀和是一种重要的预处理,能够降低算法的时

    2024年01月19日
    浏览(34)
  • 算法基础之差分和前缀和

    结论:差分是前缀和的逆运算 举例 一维差分 二维差分 用在一维子区间里的数都增减一个固定值,把对于区间内每个数的操作转换为对于差分数组中的端点的操作,时间复杂度降为o(1)。 用在二维子矩阵里的数都增减一个固定值,把对于子矩阵的每个数的操作转换为对应差分

    2024年02月07日
    浏览(45)
  • 前缀和&差分算法(Python版)

    前缀和与差分是常用的区间优化方式,其中前缀和数组可以将区间查询的时间复杂度降为常数,而差分数组则可以将区间修改的时间复杂度降为常数。 前缀和知识点简单易理解,但出题难度较大,需要根据题意挖掘出潜在的前缀和关系,建立辅助数组求解问题。 (1)一维前

    2024年04月17日
    浏览(53)
  • 【算法】—前缀和与差分详解

     前缀和指一个数组的某下标之前的所有数组元素的和( 即数列的前n项求和 ),前缀和是一种重要的 预处理 ,能够降低算法的时间复杂度,可以快速地求出某一段的和,对于处理区间之间的问题是往往十分高效 相比较其他算法而言, 前缀和更像是一种解题的技巧,一种优

    2024年02月04日
    浏览(55)
  • 【算法基础2】前缀和与差分

    前缀和是某数列的前n项数的和,而差分则可以看做是前缀和的逆运算。前缀和与差分比起是一种算法,更像是一种解决问题的思路,通过构造一个特殊的数组,就可以让我们将某些复杂的问题简化。 (1)一维前缀和 已知一个数列a[n],用S[n]来表示这个数列的前n项和数列。

    2024年04月14日
    浏览(42)
  • 【OI学习笔记】基础算法-前缀和与差分算法

    板块:基础算法、线性优化 难度:较易 前置知识:C++基础语法 在一维空间中,对于一个数据总量为 n n n 的数组 a a a ,有数据 a [ 1 ] , a [ 2 ] , a [ 3 ] , . . . , a [ n − 1 ] , a [ n ] a[1],a[2],a[3],...,a[n-1],a[n] a [ 1 ] , a [ 2 ] , a [ 3 ] , ... , a [ n − 1 ] , a [ n ] ,定义一个数组 s u m sum s u m ,

    2024年02月08日
    浏览(50)
  • C++基础算法前缀和和差分篇

    📟作者主页:慢热的陕西人 🌴专栏链接:C++算法 📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言 主要讲解了前缀和和差分算法 Ⅵ .Ⅰ前缀和 ① 一维前缀和 : ​ 就是构建一个新的数组 s ,用来存储另一个数组的和前 i 个数组元素的和。用公式表达就是: S [ i ] = a [ 0 ]

    2024年02月16日
    浏览(51)
  • C语言,洛谷题,压缩技术2.0

    题目如下:  这题用C语言实现有一些难度,要用到一个库函数,strcat(头文件是string.h),用于连接两个字符串数组,strcat(str,arr)就是将arr字符数组后面的\\0清除,再将arr字符拼接到str上。 题目指出,输入的是一个n*n大小的输入数据,可以先打印第一行后,计算第一行后,计

    2024年02月07日
    浏览(39)
  • 【有营养的算法笔记】基础算法 —— 推导证明前缀和与差分

    👑作者主页:@安 度 因 🏠学习社区:StackFrame 📖专栏链接:有营养的算法笔记 如果无聊的话,就来逛逛 我的博客栈 吧! 🌹 Hello,小伙伴们,好几天没有更新了,今天更了一篇比较“硬核的文章”。 主要内容为前缀和与差分算法的推导证明和代码实现。这篇文章博主还是画

    2024年01月21日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包