(1)合并:即将两个或多个部分进行整合,当然也可以反过来。
(2)特征:能将问题分解为能两两合并的形式。
(3)求解:将整个问题舍最优值,枚举合并点,将问题分解为左右两个部分,最后合并的两个部分的最优值得到原问题的最优值。
题目描述 :
输入两个字符串A和B,合并成一个串C,属于A和B的字符在C中顺序保持不变。如"abc"和"xyz"可以被组合成"axbycz"或"abxcyz"等。
我们定义字符串的价值为其最长回文子串的长度(回文串表示从正反两边看完全一致的字符串,如"aba"和"xyyx")。
需要求出所有可能的C中价值最大的字符串,输出这个最大价值即可
输入描述:
第一行一个整数T(T ≤ 50)。
接下来2T行,每两行两个字符串分别代表A,B(|A|,|B| ≤ 50),A,B的字符集为全体小写字母。
输出描述:
对于每组数据输出一行一个整数表示价值最大的C的价值。
样例:
输入:
2
aa
bb
a
aaaabcaa
输出:
4
5
++++++++++
DP状态分析:
作者:王清楚
链接->>:思路详细分析具体到Dp[i][j] 有两种情况(单独对于一个串 s 时):
- s[i] == s[j]:这个时候可以把s[i]和s[j]分别加到原来0的回文子序列的两端,也就是 i+1 到 j-1 这个区间的字符构成的最长回文子序列的两端,此时 dp[i][j]=dp[i+1][j−1]+2;
- s[i] != s[j]:那么在 {i}i 到 {j}j 这个区间的最长回文子序列中第 {i}i 个字符和第 {j}j 个字符一定不能同时存在,所以是可以扔掉他俩中的某一个的(并不是任意一个),此时
dp[i][j]=max(dp[i+1][j],dp[i][j−1])。
而两个串的合并就是这两种状态转移方程的子状态,具体看代码
++++++++++
AC代码(times = 973ms / 2000ms):
//区间DP
#define _CRT_SECURE_NO_WARNINGS
#include
#define LL long long
#define mem(x,y) memset(x,y,sizeof(x))
const int maxn = 52;
const int mod = 1e9 + 7;
using namespace std;
namespace needs {
template<typename T> inline void read(T& ans)
{
T x = 0, f = 1; char c = getchar();
while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
ans = f * x;
}
inline void read(string& st1)
{
char ch = getchar(); st1 = "";
while (!((ch >= 'a') && (ch <= 'z'))) ch = getchar();
while ((ch >= 'a') && (ch <= 'z')) st1 += ch, ch = getchar();
}
}using namespace needs;
int dp[maxn][maxn][maxn][maxn];
//i,j,k,l;前一个串(a串)的第 i 个字符到第 j 个字符后一个串(b串)的第 k 个字符到第 l 个字符能否组成一个回文串
int main()
{
int t;read(t);
while (t--)
{
mem(dp, 0);
string a, b; read(a), read(b);
int alen = a.size() , blen = b.size() , ans = 0;
a = ' ' + a; b = ' ' + b;//字符串后移,防止下标-1的出现
for (int lena = 0; lena <= alen; ++lena)//枚举a串的长度
for (int lenb = 0; lenb <= blen; ++lenb)//枚举b串的长度
for (int i = 1; i <= alen - lena + 1; ++i)//枚举a串区间的起始点
for (int k = 1; k <= blen - lenb + 1; ++k)//枚举b串区间的起始点
{
int j = i + lena - 1, l = k + lenb - 1;//根据各串的起始点和长度求出区间的结束点
if (lena + lenb <= 1) dp[i][j][k][l] = 1;//长度为一,本身一定是本身的回文串
else
{
//四种情况
if (a[i] == a[j]) dp[i][j][k][l] |= dp[i + 1][j - 1][k][l];
//往 a[i+1] 到 a[j−1] 和 b[k] 到 b[l] 构成的串的两端加上 a[i] 和 a[j] 两个字符
if (a[i] == b[l]) dp[i][j][k][l] |= dp[i + 1][j][k][l - 1];
//往 a[i+1] 到 a[j] 和 b[k] 到 b[l−1] 构成的串的两端加上 a[i] 和 b[l] 两个字符
if (a[j] == b[k]) dp[i][j][k][l] |= dp[i][j - 1][k + 1][l];
//往 a[i] 到 a[j−1] 和 b[k+1] 到 b[l] 构成的串的两端加上 b[k] 和 a[j] 两个字符
if (b[k] == b[l]) dp[i][j][k][l] |= dp[i][j][k + 1][l - 1];
//往 a[i] 到 a[j] 和 b[k+1] 到 b[l−1] 构成的串的两端加上 b[k] 和 b[l] 两个字符
}
if(dp[i][j][k][l]) ans = max(ans, lena + lenb);//更新答案
}
printf("%d\n", ans);//圆满结束
}
return 0;
}
题目描述 :
“lalala,我是一个快乐的粉刷匠”,小名一边快活地唱着歌,一边开心地刷着墙",兴致突然被打断,“小名,你今天如果刷不完这一栋楼的墙,那么你就等着被炒鱿鱼吧”,老板声嘶力竭的吼着。苦恼的小名因为不想被炒鱿鱼,所以希望尽量快地刷完墙,由于他本人的数学基础很差,他现在请你来帮助他计算最少完成每一堵墙需要刷多少次。每一面墙有n个段,对于每个段指定一个目标颜色ci。刚开始的时候所有的墙壁为白色,我们现在有一个刷子,刷子长度为k,刷子每次可以选择一种颜色,然后选择段数为(1~k)连续的墙段刷成选择的一种颜色。我们现在想要知道,为了把墙变成目标颜色,最少刷多少次(保证指定的目标颜色一定不为白色)。
*TIPS: 看懂的可以尝试同题型题目巩固一下 ,不懂的可以先做 简单版NC19909,同类型,相比这题简单。
输入描述: 输出描述: 样例: ++++++++++ ++++++++++ DP状态分析: 具体分析见代码 ++++++++++ AC代码(times = 4ms / 1000ms): 题目描述: 输入描述: 输出描述: 样例: ++++++++++ DP状态分析: 具体内容见代码 ++++++++++ AC代码 (times = 74ms/1000ms) 题目描述: 输入描述: 输出描述: 样例: ++++++++++ DP状态分析: 具体分析见代码 ++++++++++ AC代码(times = 3ms / 1000ms)
对于每一个案例,我们第一行包括两个整数n,k(1<=n<=100,1<=k<=50,k
输出一个数,表示小名最少刷多少次。输入一:
3 3
1 2 1
输出一:
2
输入二:
5 4
5 4 3 3 4
输出二:
3
作者: 回归的梦想
链接: 思路来源
dp[i][j]表示第i个位置到第j个位置所需要最小步数
(n为刷子的长度,k为枚举的区间断点)
第一种情况:
当 n>=len时,(len为我们当前枚举的长度),我们可以直接画完,我们在枚举断点k时,先判断a[i]==a[k],如果相等有dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]),最少操作次数就是讲第i个与第k个一起涂,所以区间[i,j]中做端点i就不用考虑了
第二种情况:
当 n//区间DP
#define _CRT_SECURE_NO_WARNINGS
#include
NC19973([HAOI]玩具取名)
某人有一套玩具,并想法给玩具命名。首先他选择WING四个字母中的任意一个字母作为玩具的基本名字。然后他会根据自己的喜好,将名字中任意一个字母用“WING”中任意两个字母代替,使得自己的名字能够扩充得很长。
现在,他想请你猜猜某一个很长的名字,最初可能是由哪几个字母变形过来的。
第一行四个整数W、I、N、G。表示每一个字母能由几种两个字母所替代。
接下来W行,每行两个字母,表示W可以用这两个字母替代。
接下来I行,每行两个字母,表示I可以用这两个字母替代。
接下来N行,每行两个字母,表示N可以用这两个字母替代。
接下来G行,每行两个字母,表示G可以用这两个字母替代。
最后一行一个长度不超过Len的字符串。表示这个玩具的名字。
一行字符串,该名字可能由哪些字母变形而得到。(按照WING的顺序输出)
如果给的名字不能由任何一个字母变形而得到则输出“The name is wrong!”输入:
1 1 1 1
II
WW
WW
IG
IIII
输出:
IN
作者: 回归的梦想
链接: 思路来源
dp[i][j][k]表示区间[i , j]是否是由k转化而来的 ,is[i][j][k]表示i是否可以用 j , k 两个字母代替
区间 [l , r], 中间点为k ,z,z1,z2 为 1~4,表示为WING(关于z, z1 , z2 可以根据代码更好理解)
如果左区间[l,k+1]可以由z1转化(即代码dp[l][k][z1]),右区间[k+1,r]可以由z2转化 (代码dp[k+1][r][z2]) , z可以用z1和z2来代替(代码(原作者错误,已更改)is[z][z1][z2]),那是不是就说明区间[l,r]是由z转化的 -》》(相当于z1,z2是一个中转站,用来将区间[l,r]与z联系在一起)
tips:结合代码理解//区间DP
#define _CRT_SECURE_NO_WARNINGS
#include
NC20238([SCOI2003]字符串折叠)
折叠的定义如下:
一个字符串可以看成它自身的折叠。记作S = S
X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
如果A = A’, B = B’,则AB = A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) = AAACBB,而2(3(A)C)2(B) = AAACAAACBB
给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
仅一行,即字符串S,长度保证不超过100。
仅一行,即最短的折叠长度。输入:
NEERCYESYESYESNEERCYESYESYES
输出:
14
dp[l][r]表示l~r的最短折叠长度
dp[l][r]=min(r-l+1,dp[l][k]+dp[k+1][r]) (l<=k#define _CRT_SECURE_NO_WARNINGS
#include
总结:根据文首判断题目类型,确认为区间DP后,将DP方程由无条件到有条件约束进行递推,得出状态转移方程后,套模板即可。
*初次接触区间DP,如有错误,请大佬帮忙指正。