题目是在n*n的棋盘上放k个车使其不互相攻击的方案数。
首先可以明确的是n*n最多只能合法地放n个车,即每一行都指派一个列去放车。
- dp[i][j]表示棋盘前i行总共放了j个车的方案数
- dp[0][0]=1
- 转移就是从第i-1行转移到第i行,对于第i行要嘛放上一个车要嘛不放,放的话有n-j-1种方法。即dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(n-j-1)。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 long long d[31][31]; 5 int main(){ 6 int t,n,k; 7 scanf("%d",&t); 8 for(int cse=1; cse<=t; ++cse){ 9 scanf("%d%d",&n,&k); 10 if(k>n){ 11 printf("Case %d: 0 ",cse); 12 continue; 13 } 14 memset(d,0,sizeof(d)); 15 d[0][0]=1; 16 for(int i=0; i<n; ++i){ 17 for(int j=0; j<=k; ++j){ 18 d[i+1][j]+=d[i][j]; 19 if(j<k) d[i+1][j+1]+=d[i][j]*(n-j); 20 } 21 } 22 printf("Case %d: %lld ",cse,d[n][k]); 23 } 24 return 0; 25 }
另外在别人博客看到排列组合的解法:结果就是C(n,k)*A(n,k),即从n行中选出k行来放车,然后这k行要指派的列就是从n列中选出k列的排列。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 long long C[31][31]; 5 int main(){ 6 for(int i=0; i<31; ++i) C[i][0]=1; 7 for(int i=1; i<31; ++i){ 8 for(int j=1; j<=i; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j]; 9 } 10 int t,n,k; 11 scanf("%d",&t); 12 for(int cse=1; cse<=t; ++cse){ 13 scanf("%d%d",&n,&k); 14 if(k>n){ 15 printf("Case %d: 0 ",cse); 16 continue; 17 } 18 long long res=C[n][k]; 19 for(int i=0; i<k; ++i) res*=n-i; 20 printf("Case %d: %lld ",cse,res); 21 } 22 return 0; 23 }