517_600

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值