UVa1638数学递推

  题意不说,直接上思路:

  这道题看起来没有思路,不清楚如何安排能够保证左边l根,右边r根,所以需要简化这道题,让思路浮现出来,

  我们摆放顺序不能是从左到右或者从右到左的顺序摆放,而从小到大不行,所以是从大到小可以,原因是摆放小的不会影响大的,

  将小的摆放左边一定能够使左边+1,同理右边也是一样,而中间则不能对左右造成影响,而从小到大的顺序会对左右产生影响,

  并且不能够很清楚的知道影响了多少(想一想,为什么)。

  所以根据上面定义了状态d(i,j,k)表示从大到小前i个,左边为j,右边为k的方案数,不难想出状态方程:

  放在左边:d(i,j,k) += d(i-1,j-1,k)

  右边: d(i,j,k) += d(i-1,j,k-1)

  中间: d(i,j,k) += d(i-1,j,k)*(i-2)

  所以总的转移方程为: d(i,j,k) = d(i-1,j-1,k) + d(i-1,j,k-1) + d(i-1,j,k)*(i-2)

  代码如下:

// UVa 1638
#include <cstdio> 
#include <cstring> 
using namespace std; 

const int maxn = 20; 

long long d[maxn+1][maxn+1][maxn+1]; 

void init() {
  memset(d, 0, sizeof(d)); 
  d[1][1][1] = 1;
  for (int i = 2; i <= maxn; ++i) 
    for (int j = 1; j <= i; ++j) 
      for (int k = 1; k <= i; ++k) {
        d[i][j][k] = d[i-1][j][k] * (i - 2);  
        if (j > 1) d[i][j][k] += d[i-1][j-1][k]; 
        if (k > 1) d[i][j][k] += d[i-1][j][k-1]; 
      }
}

int main() { 
  init(); 
  int T, n, l, r;  
  scanf("%d", &T); 
  while (T--) {
    scanf("%d%d%d", &n, &l, &r); 
    printf("%lld\n", d[n][l][r]); 
  }
  return 0; 
}

猜你喜欢

转载自www.cnblogs.com/yifeiWa/p/11412164.html