http://ac.jobdu.com/problem.php?pid=1552
题目一看就是可以用dp的,但是对我来说找状态还是最困难的一件事。参考了别人的代码,才知道如果找合适的状态,并根据我自己的理解,定义了如下状态函数和状态转移方程:
f[i][0]表示从左数第i个位置上是男生的合法排列数,f[i][1]表示从左数第i个位置上是女生的合法排列数,由此可知其该女生左边一定是女生。然而仅仅有了合法排列数是不够的,我们还要记录第i个位置上是女生的非法排列数,即该女生左边是男生,记为f[i][2]。虽然这种排列是非法的,不是答案所需的,但是在递推过程中却是必不可少的,我目前做过的几道dp题中,往往要考虑这一点,不能随意将目前不合法的状态遗漏掉,因为随着i增加,不合法的状态也有可能变为合法状态。
状态转移方程如下:
f[i][0]=f[i-1][0]+f[i-1][1],第i个位置是男生的合法种类数可直接由第i-1个位置上是男生或女生得排列数得到,即在各合法的排列最右边加一个男生,依旧合法。
f[i][1]=f[i-1][1]+f[i-1][2],第i个位置是女生的合法排列,其左边一定是女生,f[i-1][1]中各排列最右两个都是女生,但是没有考虑最右是女生而其左边是男生的情况,该状态为非法状态,由f[i-1][2]算出。
f[i][2]=f[i-1][0],第i个位置是女生的非法排列,即在i-1位置上是男生的排列右边再加一个女生构成。
我也顺便练习了一下记忆化搜索的写法。代码如下:
#include <stdio.h>
#include <memory.h>
#define MOD 1000000007
long long f[1010][3];
long long dp(int n, int k)
{
if (f[n][k]>=0) return f[n][k];
if (n==0) return 0;
else
{
if (k==0)
{
f[n][k]=dp(n-1,0)+dp(n-1,1);
}
else if (k==1)
{
f[n][k]=dp(n-1,1)+dp(n-1,2);
}
else //k==2
{
f[n][k]=dp(n-1,0);
}
f[n][k]%=MOD;
return f[n][k];
}
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
memset(f,-1,sizeof f);
f[1][0]=1;
f[1][1]=0;
f[1][2]=1;
int n;
while (scanf("%d",&n)!=EOF)
{
long long ans=(dp(n,0)+dp(n,1))%MOD;
printf("%lld\n",ans);
}
return 0;
}