蓝桥杯之分治法与动态规划

[6.1 二分查找]

已知有序的序列 int[] a,
整数 x
要求找到一个刚好比x稍微大一点的元素位置

思路:
磁体会进行递归,但是不是所有情况都递归,比如,我们只从每次结果中选出x所在范围再进行递归,这样会减少许多操作步骤,最后一步时,我们只有一个数字,如果这个数字比x大,那么这个数字的下标就是答案,否则,就是这个数字后面那个数字对应的下标。

代码:
#include<cstdio>

int f(int a[],int x,int begin,int end){
	
	if(begin+1==end) {
		if(a[begin]>x) return begin;
		return end;
	}
	
	int k=(begin+end)/2;
	if(x>=a[k]){
		return f(a,x,k,end);
	}
	return f(a,x,0,k);
}

int g(int a[],int x,int len){
	if(x>a[len-1]) return -1;
	return f(a,x,0,len);
}

int main(){	
	freopen("data.in","r",stdin);
	int a[1000],i=0;	

	while(scanf("%d",&a[i++])!=EOF);
	
	i--;
	int r = g(a,32,i);
	printf("%d",r);
}
--------------------------------------

[6.2 最大连续部分和]

数组中整数有正有负
求连续一子段,使得和最大化
2,4,-7,5,2,-1,2,-4,3
最大连续段:5,2,-1,2
最大和为8

代码:
#include<cstdio>

int g(int a[],int begin,int end){
	
	if(end==begin+1){
		if(a[begin]>0) return a[begin]; else return 0;
	}
	
	int k=(begin+end)/2;
	
	int t1=g(a,begin,k);
	int t2=g(a,k,end);
	int t3a=0;
	int sum=0;
	for(int i=k-1;i>=begin;i--){
		sum+=a[i];
		if(sum>t3a) t3a=sum;
	}
	int t3b=0;
	sum=0;
	for(int i=k;i<end;i++){
		sum+=a[i];
		if(sum>t3b) t3b=sum;
	}
	
	int t3=t3a+t3b;
	
	int max=0;
	if(t1>max) max=t1;
	if(t2>max) max=t2;
	if(t3>max) max=t3;
	
	
	return max;
}

int main(){
	freopen("data.in","r",stdin);
	int a[1000],i=0;
	while(scanf("%d,",&a[i++])!=EOF);
		
	i--;
	printf("%d",g(a,0,i));	
}
--------------------------------------

[6.3 大数乘法]

multi("5935849584045839123456789","4595805849258430535")



--------------------------------------

优化:

博弈问题:
#include <cstdio>
#include<map>

using namespace std;

map<int,int> m;

int fff(int n)
{
	if (m.count(n)!=0)
		return m[n];
		
	int t=0;


	if (n >= 1 && fff(n - 1) == 0)
		t=1;
	if (n >= 3 && fff(n - 3) == 0)
		t=1;
	if (n >= 7 && fff(n - 7) == 0)
		t=1;
	if (n >= 8 && fff(n - 8) == 0)
		t=1;
	
	if(t){
		m[n]=1;
		return 1;
	}
	
	
	return 0;
}

int main()
{	
	printf("55 : %s\n",  fff(55) ? "true" : "false");	
}
振兴中华:
#include<cstdio>

int g(int m, int n)
{
	if (m == 1 || n == 1) 
		return 1;

	return (g(m - 1, n) + g(m, n - 1)) % 10000;
}

int main()
{
	int a[100][100]={0};
	
	for(int i=1;i<100;i++){
		a[i][1]=1;
		a[1][i]=1;
	}
	
	for(int i=2;i<100;i++){
		for(int j=2;j<100;j++){
			a[i][j]=(a[i-1][j]+a[i][j-1])%10000;
		}
	}
	
	printf("%d\n",a[20][15]);
	printf("%d\n",g(20,15));
}

--------------------------------------


[6.4 缓存结果]

斐波那契 f(n) = f(n-1) + f(n-2)

解决方法:
1缓存(按需存放)
2仔细设计计算次序,可以用数组

思路:
用数组存储结果

代码:
#include<cstdio>

int main(){
	int a[10000]={0};
	a[1]=1;
	a[2]=1;
	
	for(int i=3;i<10000;i++){
		a[i]=(a[i-1]+a[i-2])%10000007;
	}
	
	int n;
	scanf("%d",&n);
	
	printf("%d\n",a[n]);
	
}
--------------------------------------

[6.5 动态规划]

城墙顶刷漆 

X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如图所示)
现需要把这些格子刷上保护漆。

你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)

比如:a d b c e f 就是合格的刷漆顺序。
c e f d a b 是另一种合适的方案。

当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。
输入数据为一个正整数(不大于1000)
输出数据为一个正整数。

例如:
用户输入:
2
程序应该输出:
24

再例如:
用户输入:
3
程序应该输出:
96

再例如:
用户输入:
22
程序应该输出:
359635897

思路:
锦囊:
fb(n)  从边缘某格开始,到与它相邻的另一个边缘格子结束
fb(n) = fb(n-1) * 2

fa(n)  从某个边缘格子开始的所有情况 
fa(n) = fb(n)      最后走对面格 
    +2*fa(n-1)     第1步走对面格  
+4*fa(n-2) 第2步走对面格

通过分析,我们发现,如果从中间的某个格子开始,那么它对面的格子只能等到某一边的格子全部涂完后再涂,因为如果在先走那一边还没涂完的情况下涂了对面格子,那么那一边没有涂的格子就没有机会再涂了。我们先考虑从边缘开始的情况。
情况1(从边缘开始,到对面格子结束):
fb(n)=fb(n-1)*2;
情况2(从边缘开始,不是到对面格子结束):
fa(n)=fb(i)*fa(n-i)*2+fb(n-i+1)*fa(i-1)*2
情况3(不从边缘开始,从第i个开始):
fa(i,n)=fb(i)*fa(n-i)*2+fb(n-i+1)*fa(i-1)*2

代码:
#include<cstdio>
#define M 1000000007

long long fb(int n){
	if(n==1) return 1;
	return fb(n-1)*2%M;
} 

long long fa(int n){	


	if(n==1) return 1;
	if(n==2) return 6;
	
	return (fb(n)+fa(n-1)*2+fa(n-2)*4)%M;
}

long long fa(int i,int n){
	
	return (fb(i)*fa(n-i)*2%M+fb(n-i+1)*fa(i-1)*2%M)*2%M;
}

long long g(int n){
	
	if(n==1) return 2; 
	long long sum=0;
	for(int i=2;i<n;i++){
		sum=(fa(i,n)+sum)%M;
	}
	sum=(sum+4*fa(n))%M;
	return sum;
}

int main(){
	for(int i=1;i<130;i++){
		int t=g(i);
		printf("%d:%d\n",i,t);
	}
 } 
优化(缓存):
#include<cstdio>
#define M 1000000007

long long fa[1000]={0};
long long fb[1000]={0};

int f(){
	fb[1]=1;
	fb[2]=2;
	fa[1]=1;
	fa[2]=6;
	for(int i=3;i<1000;i++){
		fb[i]=fb[i-1]*2%M;
		fa[i]=(fb[i]+fa[i-1]*2+fa[i-2]*4)%M;
	}
}

long long g1(int n){
	
	if(n==1) return 2; 
	long long sum=0;
	for(int i=2;i<n;i++){
		sum=((fb[i]*fa[n-i]*2%M+fb[n-i+1]*fa[i-1]*2%M)*2%M+sum)%M;
	}
	sum=(sum+4*fa[n])%M;
	return sum;
}

int main(){
	f();
	for(int i=1;i<130;i++){
		int t=g1(i);
		printf("%d:%d\n",i,t);
	}
 } 
--------------------------------------

[6.6 作业]

环形涂色

如图,组成环形的格子需要涂3种颜色。
它们的编号分别是1~14
相邻的格子不能用相同的颜色。
涂色方案的数目是:24576

当格子数目为50的时候,求涂色方案总数。

思路:
假设现在考虑最后一个格子填入什么颜色,如果它的前一个格子的颜色和第一个格子的颜色不一样,那么它有两种填法,此时,再看它前面的前面的格子如何填,因为这个格子的后面格子颜色和第一个格子颜色相同,所以相当于之前情况的一个缩小版,有f(n-2)*2种,
再考虑最后一个格子的前一个格子的颜色和第一个格子的颜色一样,那么它有一种填法,而现在再来考虑这个格子之前应该如何涂颜色,也相当于之前情况的缩小版,于是有f(n-1)*1种。

代码:
#include<cstdio>
#define M 1000000007

long long g(int n){
	if(n==1) return 3;
	if(n==2) return 6;
	
	return g(n-1)+2*g(n-2);	
}

int main(){
	long long a[1000];
	a[1]=3;
	a[2]=6;
	for(int i=3;i<130;i++){
		a[i]=a[i-1]+2*a[i-2]%M;
	}
	
	for(int i=1;i<130;i++){
		//long long t=g(i);
		long long t=a[i];
		printf("%d:%lld\n",i,t);
	}
}

posted @ 2018-04-18 15:23  abin在路上  阅读(217)  评论(0编辑  收藏  举报