d[u][v] : 用v - u次交换把第u个至第v个变成升序的方法数
t[u][k][v] : 最后一次交换k, k + 1两个位置的数的方法数
可见,
d[u][v] = sum{t[u][k][v] | k = u, .., v - 1}
t[u][k][v] = zh[v - u - 1][k - u] * d[u][k] * d[k + 1][v]
两个状态相互推算!!
递归写法思路比较清晰.
class AdjacentSwaps { public: int theCount(vector <int>); }; const int mod = 1000000007; const int N = 55; int zh[N][N]; int d[N][N], t[N][N][N]; int a[N]; int n; void cal_zh() { int i, j, k; for (i = 0; i < N; ++i) { zh[0][i] = 0; zh[i][0] = 1; } for (i = 1; i < N; ++i) { for (j = 1; j < N; ++j) { zh[i][j] = (zh[i - 1][j] + zh[i - 1][j - 1]) % mod; } } } void cal_t(int u, int v, int t); void cal_d(int u, int v); void cal_t(int u, int k, int v) { int i, j; if (t[u][k][v] != -1) return; int tmin = N, tmax = -1; for (i = u; i <= k; ++i) { tmax = max(tmax, a[i]); } for (i = k + 1; i <= v; ++i) { tmin = min(tmin, a[i]); } bool work = true; for (i = u; i <= k; ++i) { if (tmax == a[i]) continue; if (tmin < a[i]) work = false; } for (i = k + 1; i <= v; ++i) { if (tmin == a[i]) continue; if (tmax > a[i]) work = false; } if (!work || tmin > tmax) { t[u][k][v] = 0; return ; } cal_d(u, k); cal_d(k + 1, v); t[u][k][v] = (long long) zh[v - u - 1][k - u] * d[u][k] % mod * d[k + 1][v] % mod; } void cal_d(int u, int v) { int i, j, k; if (d[u][v] != -1) return ; if (u == v) { d[u][v] = 1; return ; } d[u][v] = 0; for (i = u; i < v; ++i) { cal_t(u, i, v); d[u][v] += t[u][i][v]; d[u][v] %= mod; } } int AdjacentSwaps::theCount(vector <int> p) { cal_zh(); n = p.size(); for (int i = 0; i < n; ++i) { a[i] = p[i]; } memset(d, -1, sizeof(d)); memset(t, -1, sizeof(t)); cal_d(0, n - 1); return d[0][n - 1]; }
补充:
第一个和最后一个往往是dp状态的突破口,例如这个问题中t[u][k][v]k表示最后操作的位置。