Cheerleaders UVA - 11806 啦啦隊 容次原理+二進制枚舉

在一個m 行n 列的矩形網格里放k 個相同的石子,問有多少種方法?每個格子最多放一個石子,所有石子都要用完,並且第一行、最後一行、第一列、最後一列都得有石子。 

【輸入格式】
輸入第一行爲數據組數r (T~三50) ,每組數據包含3 個整數m, n, k (2 運m乒乓2 0 ,
k~三500) 。
【輸出格式】
對於每組數據,輸出方案總數除以1000007 的餘數。
【分析】
如果題目求的是"第一行、最後一行、第一列、最後一列都沒有石子"的方案數,該有多好啊!這相當於一共只有m-2 行n-2 列, 答案自然是C((m-2)(n-2,k) 了。幸運的是,利用容斥原理,我們可以把本題轉化爲上述問題。
設滿足"第一行沒有石子"的方案集爲A , 最後一行沒有石子的方案集爲B , 第一列沒有石子的方案集爲C,最後一列沒有石子的方案集爲D , 全集爲S,則所求答案就是"在S中但不在A , B, C, D 任何一個集合中"的元素個數,可以用容斥原理求解。
在程序中,我們用二進制來表示A .B.C.D 的所有"搭配" (S 對應於"空搭配" )。如果在集合A 和B 中,相當於少了一行:如果在集合C 或D 中,相當於少了一列。假定最後剩了r 行c 列,方法數就是C(rc,k)的。

// UVa11806 Cheerleaders
// Rujia Liu
#include<cstdio>
#include<cstring>
using namespace std;

const int MOD = 1000007;
const int MAXK = 500;
int C[MAXK+10][MAXK+10];

int main() {
  memset(C, 0, sizeof(C));
  C[0][0] = 1;
  for(int i = 0; i <= MAXK; i++) {
    C[i][0] = C[i][i] = 1; // 千萬不要忘記寫邊界條件
    for(int j = 1; j < i; j++)
      C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
  }

  int T;
  scanf("%d", &T);
  for(int kase = 1; kase <= T; kase++) {
    int n, m, k, sum = 0;
    scanf("%d%d%d", &n, &m, &k);
    for(int S = 0; S < 16; S++) { // 枚舉所有16種“搭配方式”;二進制12表示選取行,34表示選取列 
      int b = 0, r = n, c = m; // b用來統計集合的個數,r和c是可以放置的行列數
      if(S&1) { r--; b++; } // 第一行沒有石頭,可以放石頭的行數r減1
      if(S&2) { r--; b++; }  
      if(S&4) { c--; b++; }
      if(S&8) { c--; b++; }
      if(b&1) sum = (sum + MOD - C[r*c][k]) % MOD; // 奇數個條件,做減法
      else sum = (sum + C[r*c][k]) % MOD;          // 偶數個條件,做加法
    }
    printf("Case %d: %d\n", kase, sum);
  }
  return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章