Live2D

枚举、模拟、递推

枚举、模拟、递推

一、枚举模拟递推

  1. 状态空间:一个实际问题的各种可能情况构成的集合

  2. 递推和递归:程序遍历状态空间

    枚举和模拟:将状态空间按照一定的顺序"直接"交给程序进行遍历的算法

  3. 按规模大小

枚举形式 一般遍历方式
多项式 循环、递推
指数 递归、位运算
排列 递归、c++ next_permutation
组合 递归+剪枝
  1. Strange Towers of Hanoi (POJ1958)

n个盘子4座塔的Hanoi问题至少需要多少步?(1<=n<=12)

分析:

n盘3塔: \(d[n] = 2*d[n-1]+1\) => \(d[n] = 2^n - 1\)

  1. 前n-1盘子 A -> B
  2. 第n盘子 A -> C
  3. 前n-1盘子 B -> C

n盘4塔:\(f[n] = min_{1\leq i<n}\{2*f[i] + d[n-i]\}\)

  1. 把i个盘子 A->B (四塔模式)
  2. 把n-i个盘子 A->D (三塔模式)
  3. 把i个盘子 B-> D (四塔模式)
  4. 考虑所有可能i取最小值

题解:

#include<iostream>
#include<cmath>

using namespace std;

int main(){
	int f[13] = {0};
	int minstep,step;

	f[1] = 1;
	for(int n=2;n<=12;n++){
		minstep = 0x3f3f3f3f;
		step=0;
		for(int i=1;i<n;i++){
			step = 2*f[i] + pow((float)2,n-i)-1;  //POJ C++的pow格式严格
			if(step<minstep)
				minstep = step;
		}
		f[n] = minstep;
	}

	for(int n=1;n<=12;n++){
		cout<<f[n]<<endl;
	}
	return 0;
}	

二、前缀和

  1. 前缀和:前面i个数的总和

  2. 对于一个给定的数列A,它的前缀和数列S

    \(s[i] = \sum_{j=1}^i A[j]\)

    数列区间数的和 = 前缀和相减

    \(sum(l,r) = \sum_{i=l}^r A[i] = S[r]-S[l-1]\)

  3. 可以扩展到二维数组中

例题:

  1. 激光炸弹(BZOJ1218)

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(N<=10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁。

输入输出格式:

输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示xi,yi,vi

输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。

输入样例:

2 1
0 0 1
1 1 1

输出样例:

1

分析:

二维数组前缀和:一定区间里价值之和

二维前缀和事例图片

\(S[i,j] = S[i-1,j] + S[i,j-1]-S[i-1,j-1]+A[i,j]\)

边长R的正方形的价值(ij为正方形的右下角)

\(P[i,j] = S[i,j] - S[i-R,j] - S[i,j-R] + S[i-R,j-R]\)

然后枚举正方形右下角找最大值

错误题解:(TLE MLE一堆 用于理解)

#include<iostream>
#define N 5000+5
using namespace std;
int A[N][N]={0};  //main函数里定义上限719 X 719
int S[N][N]={0};
int P[N][N]={0};


int main(){
	int n,r,a,b,m;
	cin>>n>>r;
	
	m= 0;
	while(n--){
		int x,y,v;
		cin>>x>>y>>v;
		A[x][y] = v;

		if(m<x) m=x;
		if(m<y) m=y;
	}

	m = (m+r>5000)?5000:m+r;

	
	for(int i=0;i<m;i++){
		for(int j=0;j<m;j++){
			a = (i-1<0)?0:i-1;
			b = (j-1<0)?0:j-1;
			S[i][j] = S[a][j]+S[i][b]-S[a][b]+A[i][j];
		}
	}


	int max = 0;
	for(int i=0;i<m;i++){
		for(int j=0;j<m;j++){
			a = (i-r<0)?0:i-r;
			b = (j-r<0)?0:j-r;
			P[i][j] = S[i][j] - S[a][j] - S[i][b] + S[a][b];
			if(P[i][j]>max)
				max=P[i][j];
		}
	}

	cout<<max;


	return 0;
}

题解:

#include<iostream>
#define N 5000+5
using namespace std;
int A[N][N]={0}; 

int main(){
	int n,r,a,b,c,m;
	cin>>n>>r;
	
	m= 0;   // 找个上限 缩短下运行时间
	while(n--){
		int x,y,v;
		cin>>x>>y>>v;
		A[x+1][y+1] += v;

		if(m<x) m=x;
		if(m<y) m=y;
	}

	m = (m+r>5000)?5000:m+r;

	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++){
			a = (i-1<0)?0:i-1;
			b = (j-1<0)?0:j-1;
			A[i][j] += A[a][j]+A[i][b]-A[a][b];
		}
	}

	int max = 0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++){
			a = (i-r<0)?0:i-r;
			b = (j-r<0)?0:j-r;
			c= A[i][j] - A[a][j] - A[i][b] + A[a][b];
			if(c>max)
				max=c;
		}
	}

	cout<<max;


	return 0;
}
  1. Tallest Cow(POJ3263)

给出N头牛的身高,和M对关系(ai与bi可以相互看见。即他们中间的牛都比他们矮)。已知最高的牛为第P头,身高为H。求每头牛的身高最大可能是多少。( \(1 \leq N,M \leq 10^4, 1 \leq H \leq 10^6\) )

输入样例:

9 3 5 5
1 3
5 3
4 3
3 7
9 8

输出样例:

5
4
5
3
4
4
5
5
5

分析:

朴素解法:数组C初始化0 ,ai与bi之间的数减一 最后设C[P] = 0 即Hi = H+C[i] \(O(NM)\)

前缀和:把对一个区间的操作转化为左右两个端点的操作,再通过前缀和得到原问题的解

​ 数组D初始化0,D[ai+1]-=1,D[bi]+=1 最后 \(C[i] = \sum_{j=1}^iD[j]\) \(O(N+M)\)

题解:

#include<iostream>
#include<map>

using namespace std;

map<pair<int,int>,bool> existed;  //处理重复的成对关系
int c[10010],d[10010];

int main(){
	int n,p,h,m;
	cin>>n>>p>>h>>m;
	
	for(int i=1;i<=m;i++){
		int a,b,t;
		cin>>a>>b;
		if(a>b){t=a;a=b;b=t;}
		if(existed[make_pair(a,b)])continue;
		d[a+1]--;d[b]++;
		existed[make_pair(a,b)] = true;
	}


	for(int i=1;i<=n;i++){
		c[i] = c[i-1] + d[i];
		cout<<h+c[i]<<endl;
	}
	return 0;
}
posted @ 2019-04-24 14:15  Duiliur  阅读(335)  评论(0编辑  收藏  举报