题目解析
题意
在m*n的矩形网格中放k个相同的石子,每个格子最多放一个石子,所有石子都必须用完,保证第一行,第一列,最后一行,最后一列都要有石子,求有多少种方法。
输入数据的组数T,每组数据包含行数M,列数N,石子数K。
输出满足题意的方法种数。
思路
运用容斥原理。全集为S,第一行没有石子的方案集为A,最后一行没有石子的方案集为B,第一列没有石子的方案集为C,最后一列没有石子的方案集为D。所求答案就是在S中,但不在A,B,C,D任何一个集合中的元素个数sum。
sum=C(m*n,k)-(A+B+C+D)+(AB+AC+AD+BC+BD+CD)-(ABC+ABD+BCD)+(ABCD)。
在程序中,用二进制表示A,B,C,D的所有搭配(S对应于空搭配),如果在集合A和B中减去一行;如果在集合C和D中减去一列。最后剩了r行c列,方法数就是C(r*c,k)。
代码
#include<stdio.h>
#include<string.h>
using namespace std;
#define mod 1000007
#define MAXD 500
int c[MAXD+10][MAXD+10];
int main(){
memset(c,0,sizeof(c));
c[0][0]=1;
for(int i=0;i<=MAXD;i++){
c[i][0]=c[i][i]=1;//初始化边界
for(int j=1;j<i;j++)//j<i由c[i][j]定义得来
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;//组合数公式的递推式
}
int T;
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
int n,m,k,sum=0;
scanf("%d%d%d",&n,&m,&k);
for(int s=0;s<16;s++){//共有16种搭配方式(0~15,二进制1111)
int b=0,r=n,a=m;//b统计集合(二进制中1)的个数,奇减偶加
if(s&1){
r--;
b++;
}
if(s&2){
r--;
b++;
}
if(s&4){
a--;
b++;
}
if(s&8){
a--;
b++;
}
if(b&1)//奇减偶加
sum=(sum+mod-c[r*a][k])%mod;//取余的时候若有减法,加一个模,避免出现负数
else
sum=(sum+c[r*a][k])%mod;
}
printf("Case %d: %d\n",cas,sum);
}
return 0;
}