题意
- 对于一个 n 个元素的错排数组 a, 如果我们令
bi = a[i] - i > 0 ~?~ + : -
||||| 现在给我们一个 n 个元素的 b [] 数组,问我们有多少个 n 个元素的排列 a [],要求 a 数组必须是错拍数组方案数。
思路
- 这题的 dp ,方法真的很奇特,
- 首先我设 dp 方程为:dp [i][j] 表示前对于字符串前 i 位置,我们把前 i 个位置中的 - 都已经选择数字填上了,还有 j 个 + 正号没有确定选择哪个数字填到对应的数字位置上,
- 这里我们需要注意:
- 如果 b [i] 为 - 我们必须在当前 i 位置决策出填那些数。
- 如果 b [i] 为 + 我们可以在当前位置先不进行决策,等待在后面决策。
- 对于 i 位置我们可以把数字 i 放到前面,我们也可以把数字 i 放到后面,
-
如果第 i 位为 + ,并且我们把数字 i 放到后面,那么有状态转移方程:
1> dp[i][j] += dp[i - 1][j - 1]
这里我们需要注意,我们把数字 i 放到后面,在当前 i 位置无法确定要填那个数字,
2> dp[i][j] += dp[i - 1][j] * j
如果我们把当前这个数字放到 i 的前面,因为数字 i 无论填到那个位置,都一定会有出现一个 + 号,因此 i 只能填到前 i-1 个位置中的 j 个还没有决策的 + 号位置,
如果当前 i 位置为 - 号,那么我们必须在 i 位置就要确定这个位置可以填那么数字,并且填上一个数字。
1> dp[i][j] +=dp[i - 1][j] * j
如果我们把这个数字放到了后面,而 i 位置位置还需要填上的数字,我们需要从 1~i 中的 j 个数字选择一个填到 i 位置(这里我们需要明白前 i 个数中有几个空位没有选择数字填上,1~i 中就有几个数字没有使用)。
2> dp[i][j] += dp[i - 1][j + 1] * (j + 1) * (j + 1)
如果这个数字 i 我们放到前面,数字 i 无论放到那个位置都一定会产一个 + 号因此我们必须把 i 放到 j+1 个空位中的某个空位上去,又因为 i 位置在当前 i 位置所填的数字必须被确定下来,我们需要从 1~i-1 中的(j+1)个数字中选择一个数字填到 i 位置上来(这些数字到一定比 i 小,因此到 i 位置一定产生的是 - 号)。
-
代码
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define sc scanf
#define pr printf
#define fi first
#define se second
#define pb push_back
#define m_p make_pair
#define Pir pair<int, int>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
/*==========ACMer===========*/
const int N = 30;
long long dp[N][N];
char s[N];
int main()
{
while (sc("%s", s + 1) != EOF)
{
int n = strlen(s + 1);
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
for (int i = 1; i <= n; i ++)
{
if (s[i] == '+')
{
for (int j = 1; j <= i; j ++)
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] * j;
}
else
{
for (int j = 0; j <= i; j ++)
dp[i][j] = dp[i - 1][j + 1] * (j + 1) * (j + 1) + dp[i - 1][j] * j;
}
}
pr("%lld\n", dp[n][0]);
}
return 0;
}