结 模拟赛 递归 动态规划 线段树

本文记录了一位程序员在编程竞赛中的解题过程,涉及三道题目,分别是利用递归解决的数轴步数问题,通过动态规划求解数字串最小改变量,以及使用线段树处理区间操作。解题策略包括递归搜索、动态规划状态转移和线段树构建与更新。
摘要由CSDN通过智能技术生成

 

目录

 

过程记录

T1解

T2解

T3解


 

过程记录

三个小时三道题

简单看了题,发现都有解题方法

oh 有朋自远方来,T3的代码是疯癫的亲爱的线段树呢

于是果断T1,目测代码最短的题目

+20min,T1代码成型,数据通过,顺利得诡异

然后T2,起初 莽 冲,思路没整理好,捏着各种算法,所以时间略长

+40min,T2结束,数据通过

最后,去招待久别的朋友线段树

+60min,长时间挠头后,把二叉树构建、区间查找、加法计算、以及两个不同操作同性质的work函数统统写了出来,然后组装

后过数据,喜洋洋,进洛谷,得50,哭唧唧

找bug,加法运算不能连等,小AC

144min,结束

惊悚发现T2少写了一个可能,但水过了

 

T1解

草率概括,给定数轴、起点和终点,按特定方式,求从起点到终点的最小步数

每一步三种方式,从x到x-1或x+1或2*x

可以联想到树,没有上下左右的dfs,所以弃dfs函数直接写递归

注意三种方法三倍数组,还有第一步在起点,起点已被走过

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+1;
int st,ed,step,loc=1,tot,a[N],b[N],fa[N];
int dfs(int x) {
	if(fa[x]!=0) dfs(fa[x]);
	tot++; }
int main() {
	scanf("%d%d",&st,&ed);
	if(st==ed) {
		printf("0");
		return 0; }
	a[1]=st,b[st]=1;
	while(step!=loc) {
		step++;
		for(register int i=1,x; i<=3; i++) {
			if(i==1) x=a[step]-1;
			if(i==2) x=a[step]+1;
			if(i==3) x=a[step]*2;
			if(x>=0&&x<=100000&&b[x]==0) {
				a[++loc]=x,fa[loc]=step;
				if(x==ed) {
					dfs(loc),printf("%d",tot-1);
					return 0; }
				b[x]=1; } } }
	return 0; }

 

T2解

草率概括++,给定一段数字串,求得到单调不递增或单调不递减的数字串的最小改变量

洛谷题干

首先,单调不递增和单调不递减性质一样

其次,满足最小改变量的数字串可能不止一个

且其中一定有一个满足:在数字串中任一个数,要么无需改变,要么改变成前一个

则这个数字串里的数字与原数字串相同

然后,推一个式子,f(i,j)表示数字串前i个数字,且第i个数字为b[j]的改变量

f(i,j)=min.f(i-1,k)+abs(a[i]-b[j]),1<=k<=j

答案是min.f(n,_)

最后,注意单调不递增和不递减,以及2^32

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e3+1;
int n,a[N],b[N];
ll k,ans,dp[N][N];
bool cmp(int a,int b) {
	return a>b; }
int main() {
	scanf("%d",&n);
	for(register int i=1; i<=n; i++)
		scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	for(register int i=1; i<=n; i++)
		dp[1][i]=abs(a[1]-b[i]);
	for(register int i=2; i<=n; i++) {
		k=dp[i-1][1];
		for(register int j=1; j<=n; j++) {
			k=min(dp[i-1][j],k);
			dp[i][j]=k+abs(a[i]-b[j]); } }
	ans=dp[n][1];
	for(register int i=2; i<=n; i++)
		ans=min(ans,dp[n][i]);
	sort(b+1,b+n+1,cmp);
	for(register int i=1; i<=n; i++)
		dp[1][i]=abs(a[1]-b[i]);
	for(register int i=2; i<=n; i++) {
		k=dp[i-1][1];
		for(register int j=1; j<=n; j++) {
			k=min(dp[i-1][j],k);
			dp[i][j]=k+abs(a[i]-b[j]); } }
	for(register int i=1; i<=n; i++)
		ans=min(ans,dp[n][i]);
	printf("%lld",ans);
	return 0; }

 

T3解

又是草率概括,给定一段数字串,多次操作

操作共两种,区间内加法,区间内比较大小

洛谷题干

线段树,建树,两种操作

附有两种简化,两种操作局限,可以直接判断区间最大值最小值,也可以合并子树

建树时,每一节点是结构体最大最小值和懒标记

输入用数组,数据开四倍

#include<bits/stdc++.h>
#define ls(x) x<<1
#define rs(x) x<<1|1
using namespace std;
const int N=1e6+1;
int n,T,a[N];
inline int read() {
	register int x=0;
	register char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x; }

struct node {
	int minn,maxx,st; } t[N*4];
inline void work1(int x) {
	t[x].maxx=max(t[ls(x)].maxx,t[rs(x)].maxx);
	t[x].minn=min(t[ls(x)].minn,t[rs(x)].minn); }
inline void work2(int x) {
	if(t[x].st) {
		int y=t[x].st;
		t[x].st=0;
		t[ls(x)].st+=y,t[rs(x)].st+=y;
		t[ls(x)].minn+=y,t[rs(x)].minn+=y;
		t[ls(x)].maxx+=y,t[rs(x)].maxx+=y; } }

void build(int x,int l,int r) {
	if(l==r) {
		t[x].minn=t[x].maxx=a[l];
		return; }
	int y=(l+r)>>1;
	build(ls(x),l,y),build(rs(x),y+1,r);
	work1(x); }

void add(int x,int l,int r,int sx,int sy,int k) {
	if(l>=sx&&r<=sy) {
		t[x].st+=k,t[x].minn+=k,t[x].maxx+=k;
		return; }
	work2(x);
	int y=(l+r)>>1;
	if(sx<=y) add(ls(x),l,y,sx,sy,k);
	if(sy>y) add(rs(x),y+1,r,sx,sy,k);
	work1(x); }

int query(int x,int l,int r,int sx,int sy,int k) {
	if(l>=sx&&r<=sy) {
		if(t[x].minn>=k) return r-l+1;
		if(t[x].maxx<k) return 0; }
	work2(x);
	int ans=0,m=(l+r)>>1;
	if(sx<=m) ans+=query(ls(x),l,m,sx,sy,k);
	if(sy>m) ans+=query(rs(x),m+1,r,sx,sy,k);
	return ans; }

int main() {
	n=read(),T=read();
	for(register int i=1; i<=n; i++) a[i]=read();
	build(1,1,n);
	for(register int i=1,x,y,z; i<=T; i++) {
		char ch[10];
		cin>>ch,x=read(),y=read(),z=read();
		if(ch[0]=='M') add(1,1,n,x,y,z);
		if(ch[0]=='A') printf("%d\n",query(1,1,n,x,y,z)); }
	return 0; }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值