枚举、模拟、递推
一、枚举模拟递推
状态空间:一个实际问题的各种可能情况构成的集合
递推和递归:程序遍历状态空间
枚举和模拟:将状态空间按照一定的顺序"直接"交给程序进行遍历的算法
按规模大小
枚举形式 | 一般遍历方式 |
---|---|
多项式 | 循环、递推 |
指数 | 递归、位运算 |
排列 | 递归、c++ next_permutation |
组合 | 递归+剪枝 |
- 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\)
- 前n-1盘子 A -> B
- 第n盘子 A -> C
- 前n-1盘子 B -> C
n盘4塔:\(f[n] = min_{1\leq i<n}\{2*f[i] + d[n-i]\}\)
- 把i个盘子 A->B (四塔模式)
- 把n-i个盘子 A->D (三塔模式)
- 把i个盘子 B-> D (四塔模式)
- 考虑所有可能i取最小值
题解:
#include#include 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
二、前缀和
前缀和:前面i个数的总和
对于一个给定的数列A,它的前缀和数列S
\(s[i] = \sum_{j=1}^i A[j]\)
数列区间数的和 = 前缀和相减
\(sum(l,r) = \sum_{i=l}^r A[i] = S[r]-S[l-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#define N 5000+5using namespace std;int A[N][N]={0}; //main函数里定义上限719 X 719int 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 5000)?5000:m+r; for(int i=0;i max) max=P[i][j]; } } cout<
题解:
#include#define N 5000+5using 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 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<
- 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#include