为啥机房的巨佬都说这是一个水题。。。绝望ing
/一个尴尬而不失友好的微笑
想到解法了,关键不知如何递推,其实很好递推啊,是我太愚蠢了。。。
开始想法:dp[i][j][k][l]其中i为男生数,j为女生数,k为所有后缀中女生-男生的最大值,l为所有后缀中男生-女生的最大值。
后来发现一件事情就是,设男生人数和女生人数的话循环变量不好写,dp方程中同时出现i-1和j-1,得写记忆化搜索(原谅我的菜),于是就改i的含义,改为总共i个人,其中女生j人,男生(i-j)人,dp方程很好写。
这里还有一个问题,就是如果采用后缀不超过k的方法,怎么保证中间部分不会存在不合法的情况呢?这个很好解决,因为一个串是一位位递推上来的,不合法的情况在处理到那一位已经被判成不合法了,不会累加到之后的状态上了。
然后强调一下max(x-1,0)这一句,怎么保证负数强制改为0,总答案是对的呢?原因是x为负数,则y会限制情况。假设x未更改为0,其上升时y必然下降,且下降的高度不会超过k,而现在x设为0后上升的高度也绝对不会超过k,所以每一个状态的种数是错的,而总答案可以保证是不变的。
最后分析一下这道题使用填表法为什么是错的。原因是上面阐述的max的问题,强制max转换时单向的,是前推向后,而后推向前肯定是错的啦(我个智障哎。。。)
update:2017/11/10
雾。。。发现自己就是一个智障。强制取max转成非负是因为总有一个空的后缀保证最大值不小于0
代码来自OTZ@27rabbit
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=155;
const int maxm=25;
const int mod=12345678;
int n,m,k;
int dp[maxn<<1][maxn][maxm][maxm];
int main()
{
scanf("%d%d%d",&n,&m,&k);
dp[0][0][0][0]=1;
for(int i=0;i<=n+m;i++)
{
for(int j=0;j<=min(m,i);j++)
{
for(int x=0;x<=min(k,j);x++)//女生减男生
{
for(int y=0;y<=min(k,i-j);y++)//男生减女生
{
if(x<k)
dp[i+1][j+1][x+1][max(y-1,0)]=(dp[i+1][j+1][x+1][max(y-1,0)]+dp[i][j][x][y])%mod;
if(y<k)
dp[i+1][j][max(x-1,0)][y+1]=(dp[i+1][j][max(x-1,0)][y+1]+dp[i][j][x][y])%mod;
}
}
}
}
long long ans=0;
for(int i=0;i<=k;i++)
for(int j=0;j<=k;j++)
ans=(ans+dp[n+m][m][i][j])%mod;
cout<<ans;
return 0;
}