给出ABC三种颜色的个数,求相邻颜色不相同,首尾颜色不相同的串的个数。
思路:
开始的时候感觉就是个搜索,但是一想简单搜索肯定超时,dp的话也没找出递推公式,竟让把记忆化搜索给忘了,悲哀。。。
dp[i][a][b][k]表示前i个位置A有a个B有b个,当前位置颜色是k的个数。
假设当前颜色是红色,也就是0(自己定)
dp[i][a][b][k]=∑dp[i−1][a−1][b][ii], ii = 1,2,3. ii != k,ii是上一个位置的颜色,不能和k相同。
从后往前搜索
#include <stack> #include <cstdio> #include <list> #include <set> #include <iostream> #include <string> #include <vector> #include <queue> #include <functional> #include <cstring> #include <algorithm> #include <cctype> #include <string> #include <map> #include <cmath> using namespace std; const int MAXN = 50 + 3; const int MOD = 1e9 + 7; char str[100]; int cnt[3]; int dp[MAXN][MAXN][MAXN][3]; int lastColor ; ///记忆化搜索 也就是算法设计中所说的备忘录方法 int DFS(int pre, int a, int b, int k)///前pre位有a个A色b个绿色,此位是k颜色的个数 { if (dp[pre][a][b][k] != -1) return dp[pre][a][b][k]; if (a < 0 || b < 0 || pre-a-b < 0) return 0; if (pre == 1 && k == lastColor) return 0; ///如果第一位和最后一位相同,0种情况 if (pre == 1) return dp[pre][a][b][k] = ((a && k == 0) || (b && k == 1) || (pre-a-b && k == 2)); ///有可能出现第一位本来已经没多余的某种颜色了,却能走到这一步。排除 ///因为枚举前一位是什么颜色的时候并没考虑那种颜色还有没有剩余 int ans = 0; for (int ii = 0; ii < 3; ii++) ///前一位是什么颜色 { if (k == ii) continue; if (k == 0) ans = (ans + DFS(pre-1, a-1, b, ii)) % MOD; if (k == 1) ans = (ans + DFS(pre-1, a, b-1, ii)) % MOD; if (k == 2) ans = (ans + DFS(pre-1, a, b, ii)) % MOD; } return dp[pre][a][b][k] = ans; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%s", str); memset(dp,-1,sizeof(dp)); memset(cnt,0,sizeof(cnt)); int len = strlen(str); for (int i = 0; i < len; i++) cnt[str[i]-'A']++; int ans = 0; for (int i = 0; i < 3; i++) ///最后一位是什么颜色 { memset(dp,-1,sizeof(dp)); lastColor=i; ans = (ans + DFS(len, cnt[0], cnt[1], i)) % MOD; for(int i=1;i<=len;i++) for(int j=1;j<=len;j++) for(int x=1;x+j<=len;x++) for(int k=0;k<3;k++) if(dp[i][j][x][k]!=-1) cout<<"dp["<<i<<"]["<<j<<"]["<<x<<"]["<<k<<"]"<<dp[i][j][x][k]<<endl; } printf("%d ", ans); } return 0; }
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define MAX 100005 #define INF 0x3f3f3f3f #define LL long long #define pii pair<string,int> #define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) const int dir[][2] = { {-1, 0}, {0, -1}, { 1, 0 }, { 0, 1 } }; using namespace std; const int MAXN = 50 + 3; const int MOD = 1e9 + 7; char str[100]; int cnt[3]; int dp[MAXN][MAXN][MAXN][3]; int firstColor,len; ///记忆化搜索 也就是算法设计中所说的备忘录方法 int DFS(int pre, int a, int b, int k)///前pre位有a个A色 b个B色,k是本次要搜索的颜色标志 { if ((k==0 && a+1 > cnt[0]) || (k==1 && b+1 > cnt[1]) || (k==2 && pre-a-b > cnt[2])) return 0; if (dp[pre][a][b][k] != -1) return dp[pre][a][b][k]; if (pre == len && k == firstColor) return 0; ///如果第一位和最后一位相同,0种情况 if (pre == len) return ((a+1<=cnt[0] && k == 0) || (b+1<=cnt[1] && k == 1) || (pre-a-b <= cnt[2] && k == 2)); ///有可能出现第一位本来已经没多余的某种颜色了,却能走到这一步。排除 ///因为枚举前一位是什么颜色的时候并没考虑那种颜色还有没有剩余 int ans = 0; for (int ii = 0; ii < 3; ii++) ///前一位是什么颜色 { if (k == ii) continue; if (k == 0) ans = (ans + DFS(pre+1, a+1, b, ii)) % MOD; if (k == 1) ans = (ans + DFS(pre+1, a, b+1, ii)) % MOD; if (k == 2) ans = (ans + DFS(pre+1, a, b, ii)) % MOD; } return dp[pre][a][b][k] = ans; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%s", str); memset(dp,-1,sizeof(dp)); memset(cnt,0,sizeof(cnt)); len = strlen(str); for (int i = 0; i < len; i++) cnt[str[i]-'A']++; int ans = 0; for (int i = 0; i < 3; i++) ///最后一位是什么颜色 { memset(dp,-1,sizeof(dp)); firstColor=i; ans = (ans + DFS(1, 0, 0, i)) % MOD; } printf("%d ", ans); } return 0; }