6.13晚,闲来无事,想着既然开始写博客,不如从简单的记起,把以前做过的一些有印象的题目记下来,于是有了此篇博客。
话不多说,进入正题。此题是一道动态规划(dp)的题目,对于大牛来说,属于信手拈来的水题,对于我这等刚踏上acm之路的新手来说,没有思路简直难于上青天。但在接触多了此类题目之后,还是很容易发现此类题目解题技巧。题目:
非递减字符串问题
总提交 : 227 测试通过 : 46
比赛描述
在非递减字符串中,
从左到右的字符依次按ASC码非递减排列,
例如abcd,其他4个字符组成的非递减字符串还有aaaa,aabb,aaab,abcc等。现在假设字符串由a、b、c、d、e、f、g、h、i、
j等10个字符组成。请你编程确定特定长度的非递减字符串的数目。在非递减字符串中,
从左到右的字符依次按ASC码非递减排列,例如abcd,其他4个字符组成的非递减字符串还有aaaa,aabb
,aaab,abcc等。现在假设字符串由a、b、c、d、e、f、g、h、i、j等10
个字符组成。请你编程确定特定长
度的非递减字符串的数目。
在非递减字符串中,从左到右的字符依次按ASC码非递减排列,例如abcd,其他4个字符组成的非递减字符串还有aaaa,aabb,aaab,abcc等。现在假设字符串由a、b、c、d、e、f、g、h、i、j等10个字符组成。请你编程确定特定长度的非递减字符串的数目。
输入
输入包括多个测试用例,首先给出测试用例数P(1≤P≤1000),接着给出P个测试用例。每一个测试用例包括一行,给出以空格分隔的两个整数,第1个整数表示测试用例序号,第二整数表示字符串的长度N(1≤N≤64)。
输出
输出包括多行,对于每个测试用例输出一行,输出以空格分隔的两个整数,第1个整数为测试用例序号,第二整数给出长度为N的非递减字符串数目。
样例输入
2
1 4
2 3
样例输出
1 715
2 220
解题方法:首先构造dp数组,dp[i][j] i指长度为i , j范围是1-10,解释:dp[i][1]是指字符串长度为i时,以字符串a为结尾的字符串的个数,最终输入n的返回结果即为dp[1]到dp[10]的累加。
然后最为关键的一步是状态方程的推导,此题的状态方程为:dp[i][j]=dp[i][j-1]+dp[i-1][j]。以dp[3,2]=dp[3,1]+dp[2,2]为例,dp[3,2]指的是长度为3末尾是b的字符串个数,dp[2,2]指的是长度为2末尾是b的字符串个数,dp[3,1]指的是长度为3末尾是a的字符串个数,长度为3末尾为b的个数就等于长度为2末尾为b的个数(由二到三只需在末尾加上b即可数量相同)加上长度为3末尾为a的个数(即将末尾的a转换为b)。
最后贴上代码:
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=65;
long long dp[maxn][maxn]; //构造一个dp数组
int main() //dp[i][j] i指长度为i j范围是1-10 意思指 以a(1) 结尾的字符串的个数
//而所求结果就是 dp[n][1]-dp[n][10]的累加
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=10;i++)
dp[1][i]=1;
int T;
cin>>T;
while(T--)
{
long long Case,n,res=0;
cin>>Case>>n;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=10;j++)
dp[i][j]=dp[i][j-1]+dp[i-1][j];
}
for(int j=1;j<=10;j++)
res+=dp[n][j];
cout<<Case<<" "<<res<<endl;
}
}
重复上文提过的观点,在见多识广的大牛前,这些题目还真的是水题,但对于初学者来说还是得花费时间去理解掌握。总结下来,还是刷题量太少,见识太短浅。解决方法唯有刷题刷题再刷题。
仔细评估一下自己现在的水平,还是急需提高,老话重提,征途漫漫啊。
特记下,以备后日回顾。