题目大意:
给出N,L,R,表示现在有N个长度互不不同的木棍,现在要求将他们排列,使得从左边向右边看能看见L根, 从右边向左边看能看见R根,问满足这样条件的排列有多少种
数据范围:1 <= L,R <= n <= 20;
大致思路:
刚开始还以为这是一个排列组合的问题,后来想了一下发现时一个DP,
首先我们这样考虑:
设dp[ n ][ L ] [ R ] 表示当前用n根木棍满足条件L,R的不同排列数
每次向n根排列好的木棍中加入新的一根的时候,视作新加入的那一根比之前所有排列好的都要短
那么对于dp[ n + 1 ][ L ][ R ]它的上一状态可以来自于 dp[ n ][ L ] [ R ] (即新的一根插入后不影响L和R,由于新加入的一根比之前的所有木棍都短,只需要插入在原来n根的任意位置即可, 另外还可以来自于上一状态 dp[ n ] [ L - 1 ][ R ]即新加入的放在最左边, 同样的还有来自于状态 dp[ n ][ L ][ R - 1 ]即新加入的放在最右边,那么我们可以得到状态转移方程
dp[ n ][ L ] [ R ] = (n - 2)*dp[ n - 1 ][ L ][ R ] + dp[ n - 1 ][ L - 1 ][ R ] + dp[ n - 1 ][ L ][ R - 1 ];
而n,L,R为1,2的时候都是很容易算的,这样就可以动态规划解决这个问题
代码如下:
Result : Accepted Memory : 0 KB Time : 3 ms
/* * Author: Gatevin * Created Time: 2014/7/27 15:21:24 * File Name: hehe.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; lint dp[22][22][22]; int main() { int n1,n2; memset(dp, 0, sizeof(dp)); dp[1][1][1] = 1; dp[2][1][2] = 1; dp[2][2][1] = 1; dp[3][1][3] = 1; dp[3][3][1] = 1; dp[3][2][2] = 2; dp[3][1][2] = 1; dp[3][2][1] = 1; for(int i = 4; i <= 20; i++) { for(int j = 1; j <= i; j++) { for(int k = 1; k <= i; k++) { dp[i][j][k] = dp[i - 1][j][k] * ( i - 2) + dp[i - 1][j - 1][k] + dp[i - 1][j][k - 1]; } } } int n; int t; cin>>t; while(t--) { cin>>n>>n1>>n2; cout<<dp[n][n1][n2]<<endl; } return 0; }