2021-1-29

线段树

一种略复杂,但是很高效的查找方法。总体思路类似于二分法。对于一组数据每一次都分成两份,并且由此建立一个二叉树。个人感觉比较有趣的是刚好凭借靠着这种2和3整除2都为1这种数字关系可以找回自己的上一个节点。比较简单的应用就是例题POJ - 1166。求一串数字的指定区间的和。从根节点1开始。节点1储存所有的和,2储存1前半部分的和,3储存1后半部分的和。从这里继续,4储存2前半部分的和,5储存2后半部分的和。由此类推一次向下展开,一直到该线段树本身的和是指向单个数字的时候,也就是找到了叶节点,在这里结束。
带上自己写的代码,有利于理解

#include<stdio.h>
#define N 50010
int t,n,k[N],a,b,mm,nn,kk[N];
char m[10];
struct tree{
    int l,r,sum=0;	
}trees[N*4];
void build(int l,int r,int i){
	if(l==r){
		trees[i].l=trees[i].r=l;
		trees[i].sum=k[l];
		kk[l]=i;//标记叶节点所处的位置。 
		return;
	}
	else{
		int mid=l+r>>1;
		build(l,mid,i<<1);
		build(mid+1,r,(i<<1)+1);
		trees[i].sum=trees[i<<1].sum+trees[(i<<1)+1].sum;
		trees[i].l=l;
		trees[i].r=r;
	}
}
void rebuild(int x,int b){
	while(x>0){
		x=x>>1;
		trees[x].sum+=b;
	}
}
void result(int l,int r,int i){
	if(l<=trees[i].l&&r>=trees[i].r){
		nn+=trees[i].sum;
	}
	else{
		int mid=(trees[i].l+trees[i].r)>>1;
		if(mid>=l&&mid<r){
		    result(l,mid,i<<1);
		    result(mid+1,r,i<<1|1);
		}
		else if(mid<l){
			result(l,r,i<<1|1);
		}
		else if(mid>=r){
			result(l,r,i<<1);
		}
	}
}
int main(void){
	scanf("%d",&t);
	while(t--){
		printf("Case %d:\n",++mm);
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%d",&k[i]);
		}
		build(0,n-1,1);
		/*for(int i=0;i<30;i++){
			printf("* %d %d *%d**%d\n",trees[i].l,trees[i].r,i,trees[i].sum);
		}*/
		while(~scanf("%s",m)&&m[0]!='E'){
			scanf("%d%d",&a,&b);
			a--;
			if(m[0]=='A'){
				trees[kk[a]].sum+=b;
				rebuild(kk[a],b);
			}
			else if(m[0]=='S'){
				trees[kk[a]].sum-=b;
				rebuild(kk[a],-b);
			}
			else if(m[0]=='Q'){
				b--;
				nn=0;
				result(a,b,1);
				printf("%d\n",nn);
			}
			
		}
	}
} 

其实接下来还有好多种线段树的关系,比如找最大值,找最大子序列等等,视不同的情况,复杂程度也会不一样,所以主要了解一下数学模型是什么样子,具体题目具体分析。

贪心

这怎么说呢,并不是一种数学模型,而是一种思考的方式,单独列出来其实我个人觉得可能,大概,也许,应该,maybe没太大必要吧。这种就是自己在做题的时候有没有考虑到这一点,说是要有一种固定的套路或者模型的话是没有的。总体来说,大体是,当一个问题可以拆解为各个不相互影响的小问题的时候,拆分开来并且解出每个小问题的最优解,之后再把每个最优解组合起来。

dp

说实话,这几天的贪心,dp还有刚学的hash。尤其是dp和hash,都是一知半解的。还需要再理解理解。怎么说呢,有一些例子我会很好懂,但另外一些,开始抽象了,是慢慢有点难理解了。这里dp的话最直观的问题,也是我最先理解的问题就是例题HDU 2602。听说这个问题和背包问题差不多。不过我反正是对该问题的模型是理解了的,然后再看别的例题的时候,发现模型一样,但是多多少少还是有点不理解为什么可以这样。不过dp的核心思路应该还是局部最优解以及迭代递归。类似于这一题中,每一块骨头有自己不同的重量和不同的价值,问在固定的背包容量中,如何装下最大总价值的骨头。大体思路我感觉也是贪心。先放第一块骨头,然后背包的容量从零到最大值,放不下的丢掉,放得下就放下。之后再放第二块骨头,背包的容量也从零到最大值开始,放不下丢掉,放得下但只能放下第一块或者第二块的时候,考虑该背包容量的情况下,放哪个价值最高,取最高的那一块。如果两块都能放下,那最好,就都放下。如此向后迭代的话,总结来说就是,遇到一块骨头,能放下就放下,放不下就判断,是拿掉一块骨头好,还是就不要这个东西了。这里放张图,还有我的代码,比较好理解,真的我看了图和代码,就瞬间顿悟了。
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
int main(void){
	int t,m,n,w[1005],v[1005];
	static int mm[1005][1005];
	scanf("%d",&t);
	while(t--){
		memset(mm,0,sizeof(mm));
		scanf("%d%d",&m,&n);
		for(int i=0;i<m;i++){
			scanf("%d",&v[i]);
		}
		for(int i=0;i<m;i++){
			scanf("%d",&w[i]);
		}
		for(int i=1;i<=m;i++){
			for(int j=0;j<=n;j++){
				if(j>=w[i-1]){
					mm[i][j]=max(mm[i-1][j],mm[i-1][j-w[i-1]]+v[i-1]);
				}
				else{
					mm[i][j]=mm[i-1][j];
				}
			}
		}
		printf("%d\n",mm[m][n]);
	}
}

之后还有一题是什么记忆化搜索,我也感觉挺妙的,直接放代码吧。

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
int m,n,k[100][100],num[100][100],mm=0;
int x[4]={0,0,1,-1};
int y[4]={1,-1,0,0};
int find(int a,int b){
	int z=1;
	if(num[a][b]){
		return num[a][b];
	}
	else{
		for(int i=0;i<4;i++){
			int ii=a+x[i];
			int jj=b+y[i];
			if(ii>=0&&ii<m&&jj>=0&&jj<n&&k[ii][jj]<k[a][b]){
				z=max(z,find(ii,jj)+1);
			}
		}
		num[a][b]=z;
		return z;
	}
}
int main(void){
	scanf("%d%d",&m,&n);
	memset(num,0,sizeof(num));
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			scanf("%d",&k[i][j]);
		}
	}
	for(int i=0;i<m;i++){
		for(int j=0;j<n;j++){
			num[i][j]=find(i,j);
			if(mm<num[i][j]){
				mm=num[i][j];
			}
		}
	}
	printf("%d\n",mm);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值