在写题目之前先来介绍一下记忆化搜索。
算法上依然是搜索的流程,但是搜索到的一些解用动态规划那种思想和模式保存。一般来说,动态规划总要遍历所有的状态,而搜索可以排除一些无效的状态。最最最最最主要的是,搜索还可以剪枝,可以减去大量的不必要的状态,这样在空间的占用率上会比动态规划低很多。
记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求一个解的状态就要将其保存下来,以后在遇到这个状态的时候就没有必要再求一遍了。这种算法综合了搜索和动态规划两方面的优点。
既然说到这里就顺便总结一下动态规划。
动态规划:
动态规划就是一个最优化问题,先将问题分解为多个子问题,并且在这些分解的子问题自身就是最优的条件下才可以得出我们要解决的问题的最优方案,要不然就可以找到一个更优的解来替代这个解,得出新的最优子问题,这样就与前提相矛盾了。
动态规划不同于贪心算法,贪心是从局部最优来解决问题,而动态规划是全局最优的。贪心贪出来的不一定是全局最优的。动态规划不能在子问题还没有得到最优解的情况下就做出决策,必须要等全部子问题得到最优解之后再对当下情况做出决策,所以动态规划往往可以用一个或多个递归式来描述。而贪心是先做出一个决策,然后再去解决子问题,这便是贪心与动态规划的不同。
一般在面对动态规划问题时的步骤:
(1)首先确定最优子结构(问题),还有重叠子问题,这是动态规划最大的两个特征。
(2)写出动态规划的状态转移方程
(3)自底向上求解问题,先将最小规模的子问题的最优解求出来,一般都用一张表来记录下所求出的解,到后来遇到同样的子问题就可以直接查表找答案了(俗称打表)
(4)通过迭代得出最后的答案
动态规划最重要的是(个人认为):一定要会用一个数组或者其他存储结构存储所得到的子问题的解
动态规划的一种变形就是记忆化搜索,就是根据动态规划转移方程写出递归式,然后在函数开头直接返回以前计算的结果当然这样做也需要一个存储结构记录下之前计算出的结果,所以称为记忆化搜索。
接下来给出其定义。
记忆化搜索/递归式动态规划:
思想:在搜索过程中,会有很多的重复计算,如果我们可以记录下得出答案,就可以减少重复搜索量。
适用范围:它解决的是重复计算,而不是重复生成。也就是说,这些搜索必须是在搜索扩展路径的过程中分步计算的题目,也就是“搜索答案与路径相关”的题目,而不能是搜索一个路径之后才能进行计算的题目,一定要分步计算,并且在搜索过程中,一个搜索结果必须可以建立在同类型问题的结果上,也就是类似于动态规划解决的那种问题。
这问题,不是单纯生成一个走步方案,而是生成一个走步方案的代价等,而且每走一步,在搜索树/图中生成一个新状态,都可以分步计算。
实现:
(1)通过一个表记录已经存储下的搜索结果,一般用哈希表实现。
(2)状态表示,由于是要用哈希表实现,所以状态最好是用数字表示,常用的方法是把一个状态写成一个p进制数字,然后把这个数字对应的十进制数作为状态(该题目不需要用此方法)
(3)在每一状态搜索的开始,高效的使用哈希表搜索这个状态是否出现过,如果出现过,直接调用答案,回溯
(4)如果没有,则用正常方法搜索
记忆化搜索类似于动态规划,它是倒做的“递归式动态规划”。
最后,简单介绍一下dfs记忆化搜索的特点:
核心的搜索算法一般使用递归形式,而且递归函数的参数往往直接和问题的求解对象有关联,(就如下面这个题目 五个变量全与求解对象有关),还有就是在搜索函数的开头一般先放上剪枝的条件,起回溯作用,不然函数会无限递归。在执行递归时,一般先对当前的局面状态进行保存,在递归函数返回时,在进行恢复,类似回溯。
dfs的参数表示的就是局面的一种状态。
Description
Beaver Bindu has N cupcakes. Each cupcake has one of three possible colors. In this problem we will represent the colors by uppercase letters ‘A’, ‘B’, and ‘C’. Two cupcakes of the same color are indistinguishable. You are given a String cupcakes consisting of exactly N characters. Each character in cupcakes gives the color of one of Bindu’s cupcakes.
Bindu has N friends, sitting around a round table. She wants to give each friend one of the cupcakes. Moreover, she does not want to give cupcakes of the same color to any pair of friends who sit next to each other.
Let X be the number of ways in which she can hand out the cupcakes to her friends. As X can be very large, compute and return the value (X modulo 1, 000, 000, 007).
Input
The first line contains one integer T(1 ≤ T ≤ 60)—the number of test cases. Each of the next T lines contains one string. Each string will contain between 3 and 50 characters, inclusive. Each character in the string will be either ‘A’, ‘B’, or ‘C’.
Output
For each test case. Print a single number X — the number of ways in which she can hand out the cupcakes to her friends.
Sample Input
3
ABAB
ABABA
ABABABABABABABABABABABABABABABABABABABABABABABABAB
Sample Output
2
0
2
【题意】: n块蛋糕 ABC三种颜色 分给一个圆桌n个人 相邻两个人的颜色不能相同,返回能给朋友的方式数
**【思路】:**五维数组 1维代表开始的颜色 2,3,4维代表三种颜色使用的次数 5维代表上一次使用的蛋糕是哪一种
然后用记忆化搜索dfs
【代码】:
//n块蛋糕 ABC三种颜色 分给一个圆桌n个人 相邻两个人的颜色不能相同
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=55;
const long long M=1000000007;
long long dp[4][N][N][N][4];
//五维数组 1维代表开始的颜色 2,3,4维代表三种颜色使用的次数 5维代表上一次使用的蛋糕是哪一种
int len,tmpa,tmpb,tmpc;
//tmpa A颜色的数量 tmpb B颜色的数量 tmpc C颜色的数量
int dfs(int start,int a,int b,int c,int t)
{
if(a>tmpa||b>tmpb||c>tmpc) //任意一种颜色的蛋糕被吃光
return 0;
if(a+b+c==len&&t==start) //全部分配完并且结尾等于开头
return 0;
if(dp[start][a][b][c][t]!=-1) //记忆化搜索的剪枝
return dp[start][a][b][c][t];
if(a+b+c==len&&t!=start) //最终完美状态
return 1;
long long tmpp=0;
if(t==1)
{
tmpp=(tmpp%M+dfs(start,a,b+1,c,2)%M)%M;
tmpp=(tmpp%M+dfs(start,a,b,c+1,3)%M)%M;
dp[start][a][b][c][t]=tmpp;//记忆
return tmpp;
}
if(t==2)
{
tmpp=(tmpp%M+dfs(start,a+1,b,c,1)%M)%M;
tmpp=(tmpp%M+dfs(start,a,b,c+1,3)%M)%M;
dp[start][a][b][c][t]=tmpp;
return tmpp;
}
if(t==3)
{
tmpp=(tmpp%M+dfs(start,a+1,b,c,1)%M)%M;
tmpp=(tmpp%M+dfs(start,a,b+1,c,2)%M)%M;
dp[start][a][b][c][t]=tmpp;
return tmpp;
}
}
int main()
{
ios::sync_with_stdio(false);
int T;
char s[1000];
cin>>T;
while(T--)
{
tmpa=0,tmpb=0,tmpc=0;
memset(s,0,sizeof(s));
memset(dp,-1,sizeof(dp));
cin>>s;
long long ans=0;
len=strlen(s);
for(int i=0;i<len;i++)
{
if(s[i]=='A')
tmpa++;
if(s[i]=='B')
tmpb++;
if(s[i]=='C')
tmpc++;
}
ans=(ans%M+dfs(1,1,0,0,1)%M)%M;
ans=(ans%M+dfs(2,0,1,0,2)%M)%M;
ans=(ans%M+dfs(3,0,0,1,3)%M)%M;
cout<<ans<<endl;
}
return 0;
}