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表示最后操作的位置。