蓝桥杯之分治法与动态规划
[6.1 二分查找]
已知有序的序列 int[] a,
整数 x
要求找到一个刚好比x稍微大一点的元素位置
思路:
磁体会进行递归,但是不是所有情况都递归,比如,我们只从每次结果中选出x所在范围再进行递归,这样会减少许多操作步骤,最后一步时,我们只有一个数字,如果这个数字比x大,那么这个数字的下标就是答案,否则,就是这个数字后面那个数字对应的下标。
代码:
[6.2 最大连续部分和]
数组中整数有正有负
求连续一子段,使得和最大化
2,4,-7,5,2,-1,2,-4,3
最大连续段:5,2,-1,2
最大和为8
代码:
[6.3 大数乘法]
multi("5935849584045839123456789","4595805849258430535")
略
--------------------------------------
优化:
博弈问题:
[6.4 缓存结果]
斐波那契 f(n) = f(n-1) + f(n-2)
解决方法:
1缓存(按需存放)
2仔细设计计算次序,可以用数组
思路:
用数组存储结果
代码:
[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
代码:
[6.6 作业]
环形涂色
如图,组成环形的格子需要涂3种颜色。
它们的编号分别是1~14
相邻的格子不能用相同的颜色。
涂色方案的数目是:24576
当格子数目为50的时候,求涂色方案总数。
思路:
假设现在考虑最后一个格子填入什么颜色,如果它的前一个格子的颜色和第一个格子的颜色不一样,那么它有两种填法,此时,再看它前面的前面的格子如何填,因为这个格子的后面格子颜色和第一个格子颜色相同,所以相当于之前情况的一个缩小版,有f(n-2)*2种,
再考虑最后一个格子的前一个格子的颜色和第一个格子的颜色一样,那么它有一种填法,而现在再来考虑这个格子之前应该如何涂颜色,也相当于之前情况的缩小版,于是有f(n-1)*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);
}
}