LightOJ1005 Rooks(DP/排列组合)

题目是在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 }