题解
使用状压dp d[i][j][k]表示当前长度为i最后的位置使用j并且已经使用的状态为k的情况下方案数量
从前向后推 枚举每个位置 枚举当前位置使用的数字枚举下个位置使用的数字枚举所有状态进行转移 如果满足条件则直接加上当前数量
注意2倍关系是双向的
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int MAXN = 16;
int d[MAXN][MAXN][1 << 16]; //d[i][j][k]表示长度为i以j为结尾使用状态为k的数量
char s[MAXN];
int two(int a, int b)
{
if (a % b == 0 && a / b == 2)
return 1;
if (b % a == 0 && b / a == 2)
return 1;
return 0;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n;
cin >> n;
scanf("%s", s + 1);
for (int i = 1; i <= n; i++)
d[1][i][(1 << i - 1)] = 1;
for (int i = 1; i < n; i++) //前推后
{
for (int j = 1; j <= n; j++) //当前位置所用
for (int k = 1; k <= n; k++) //下个位置所用
if (j != k && s[i] - '0' == two(j, k)) //数字不相等且满足题意
for (int p = 0; p < (1 << n); p++) //当前状态
if ((p & (1 << k - 1)) == 0)
d[i + 1][k][p | (1 << k - 1)] = (d[i + 1][k][p | (1 << k - 1)] + d[i][j][p]) % MOD;
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + d[n][i][(1 << n) - 1]) % MOD;
cout << ans << endl;
return 0;
}