模拟、枚举与贪心2

这篇博客探讨了几种不同的算法问题,包括寻找排列中奇数长度子序列的中位数、激光炸弹投放策略、最短连续子序列求和以及扫雷游戏的解决方案。涉及前缀和、二维数组维护、尺取法等算法思想,强调区间查询、边界处理和逆向思维的重要性。
摘要由CSDN通过智能技术生成

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

[CQOI2009]中位数图

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

 说明:排列指n个数每个数只能出现一次。子序列指选其中的若干数且它们的相对位置不发生变化的序列,而子串绝对是连续地选择了一段(如:1、 4、 2、 5,则其中的子序列可以是1 2,子串可以是1 4),但题目中加上了“连续”这一约束,使得有了子串的效果。

我们想,在寻找中位数序列时,考虑这些数究竟多大其实并没有用,其实只要知道一个区间内小于和大于目标数的个数是否相等即可,前缀和立刻浮现于脑海。不妨使大于目标数的数为1,小于目标数的数为-1,目标数为0,现在问题转化为给一个数列,寻找使其和等于0的区间,同时这个区间还必须包含0(目标数)。我们可以以0为分界点,对左右分别求前缀和同时用数组cnt[i+max](+max防止越界)记录等于该值的个数,则寻找使其等于0的区间,只需通过自己的区间值对相反数的区间进行累加即可。举例,3为中位数:

排列        1        4        3        0        6        2

数组        -1       1        0       -1        1       -1

前缀和     0        1        0       -1        2        1        此处前缀和是以3为基准向外进行前缀和

错误点:搭配的情况没有处理好。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<algorithm> 
using namespace std;
typedef long long ll;
const ll maxn=100005;
const ll mod=1000000007;
ll n,b,x,pos,ans=1,a[maxn],sum[maxn],cnt[maxn<<1];
int main(){
	cin>>n>>b;
	for(int i=1;i<=n;++i){
		scanf("%d",&x);
		if(x>b) a[i]=1;
		else if(x==b){
			a[i]=0; pos=i;
		}
		else a[i]=-1;
	}
	for(int i=pos-1;i>=1;--i){
		sum[i]=sum[i+1]+a[i];
		cnt[sum[i]+maxn]++;
		if(sum[i]==0) ans++;
	}
	for(int i=pos+1;i<=n;++i){
		sum[i]=sum[i-1]+a[i];
		ans+=cnt[maxn-sum[i]];
		if(sum[i]==0) ans++;
	}
	cout<<ans;
	return 0;
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

激光炸弹

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。

现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。

激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。

若目标位于爆破正方形的边上,该目标将不会被摧毁。 

 思路:二维前缀和的维护与查询。

错误点:进行查询操作时,区间起点与区间终点必须严格限制在最大范围之内,否则容易导致出错。

注意点:不包含边界暗示前缀和重要的易错点,减去r后定位的值其实不要了,即只求得了r-1的区间值之和;其实一个二维数组足够维护前缀和了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<algorithm> 
using namespace std;
typedef long long ll;
const ll maxn=5005;
const ll mod=1000000007;
ll n,r,xx,yy,x,y,v,ans,f[maxn][maxn],a[maxn][maxn];
int main(){
	cin>>n>>r;
	xx=r; yy=r;
	for(int i=0;i<n;++i){
		scanf("%d%d%d",&x,&y,&v);
		x++; y++;
		f[x][y]=v;
		xx=max(xx,x);
		yy=max(yy,y);//?
	}
	for(int i=1;i<=xx;++i)
		for(int j=1;j<=yy;++j)
			f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-1]+f[i][j];
	for(int i=0;i<=xx-r;++i)
		for(int j=0;j<=yy-r;++j)
			ans=max(ans,f[i+r][j+r]-f[i][j+r]-f[i+r][j]+f[i][j]);
	cout<<ans;
	return 0;
}

带权中位数的两种解法

        有若干个排列在一条直线上的点pi,每个点上有ai个人,找出一个人使得所有人移动到这个点的位置上的总距离最小。

        假设向答案点左方移动一位,观察对距离和的影响: 

dist_{x-1}=dist_{x}-\sum_{i=1}^{x-1}a_{i}len_{i}+\sum_{i=x}^{x+1}a_{i}len_{i}

        通过以上公式分析可知,当找到位于人数刚好大于中位数的位置即可得最小距离。因为不管是选取前缀和小于中位数的地点还是前缀和远大于中位数的地点,都会因为位置转移使更多的人走更多的距离。

拓展:有若干个排列在一条直线上的点pi,每个点上有ai个人,找出一个人使得所有人移动到这个点的位置上的距离的平方和最小。

         对于这一类型的题,可以考虑从某点变化到另一个点的影响,即先假设x为其中一点,通过求x+1比x多走的距离的公式,分析造成影响的因素。注意过程中,可以通过化简式子来降低循环次数。

 链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

Subsequence

A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

给定N个非负整数,求解至少需要选多少个连续的数,它们的和不小于给定的整数S,特别的,若没有解,则输出0

 思路:利用尺取或队列。

错误点:最终是求最小值,不要忘了在更新ans的时候判断是否更小;尺取的话可以想成“先让头向前(提前循环,内循环),尾巴跟上(外循环)”。

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

丢手绢

“丢~丢~丢手绢,轻轻地放在小朋友的后面,大家不要告诉她,快点快点抓住她,快点快点抓住她。”

牛客幼儿园的小朋友们围成了一个圆圈准备玩丢手绢的游戏,但是小朋友们太小了,不能围成一个均匀的圆圈,即每个小朋友的间隔可能会不一致。为了大家能够愉快的玩耍,我们需要知道离得最远的两个小朋友离得有多远(如果太远的话牛老师就要来帮忙调整队形啦!)。

因为是玩丢手绢,所以小朋友只能沿着圆圈外围跑,所以我们定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离。

思路:尺取法、求最为接近周长一半的值。

错误点:

        1、整数先除2再乘2结果会改变;

        2、尺取时,尾部作为外层循环在循环尾需要对数据施加影响;

        3、对于圆的处理一定要考虑开头结尾处的影响!对于此种情况可以对队头(内层循环)进行取模,从而保证每一次遍历的结果都是对每一位小朋友而言的。

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

[SCOI2005]扫雷MINE

相信大家都玩过扫雷的游戏。那是在一个n*m的矩阵里面有一些雷,要你根据一些信息找出雷来。

万圣节到了 ,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字 表示和它8连通的格子里面雷的数目。

现在棋盘是n×2的,第一列里面某些格子是雷,而第二列没有雷,如下图: 由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。

错误点:dfs暴力搜索模拟结果再根据第二列中的信息判断模拟得是否正确。最要命的是设了个全局变量,在回溯回来以后没有相应的更改全局变量的值导致错了好久……更别提本来思路就不恰当wwww……后来在改正上述错误的基础上发现边界点处由于只需加步数而不需加循环而把答案多算了两遍,除了个2居然AC了……

正确思路:我们一定要学会逆向思维。既然直接模拟数据再经过信息判断会很麻烦,那不如直接用给定的信息去预测地雷的位置和数量。说得轻巧……那接下来还是具体讲一遍好啦。

设a对应地图中第一列,b对应第二列,则在一般情况下b[i]=a[i-1]+a[i]+a[i+1],将i替换为i-1得,a[i]=b[i-1]-a[i-1]-a[i-2],其实就是根据地图上L形区域对a[i]处的点求解啦。这个时候若求得的解不是0或1,就说明地图信息的逻辑出错啦。可能你会问,还需要一开始的a的信息啊。别急别急,难道你对暴力循环生成数据有那么大的执念嘛……(一看就是懒得动脑,数据范围再大点看你咋办)难道不能从b数列中得出a的信息吗?比如b的第一个数为0,那说明a的前两个区域铁定没有地雷啦;b的第一个数为2,那a前两个区域铁定都有地雷啦;至于为1嘛……咳咳还是需要分类讨论的。其实这也没啥嘛不就是a中第一个区域有地雷和第二个区域有地雷的区别嘛。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值