题目链接:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2906
本题我本来想逐步分类四个角的情况。但是发现分类之间是没有重复的,而内部是有重复的。错误代码如下。
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <iostream>
using namespace std;
#define MOD 1000007
#define Maxn 405
int c[Maxn+10][Maxn+10];
void init()
{
memset(c,0,sizeof(c));
for(int i=0;i<Maxn;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 solve(int m,int n,int k)
{
int ans = 0;
int total = m * n;
//不可能
printf("ans1 = %d\n",ans);
if(k > total) return ans;
//无角放棋
if(k >= 4 && total >= 8 && m>2 && n>2)
{
if((k-4)<=(total-8))
{
ans = (ans + (m-2)*(m-2)*(n-2)*(n-2)*c[total-8][k-4])%MOD;
}
}
printf("ans2 = %d\n",ans);
//只有一角放棋
if(n>2 && m>2 && k>=3 && total>=6)
{
if((k-3)<=(total-4))
{
ans = (ans + 4*(m-2)*(m-2)*c[total-6][k-3])%MOD;
}
}
printf("ans3 = %d\n",ans);
//只有二角放棋
if(1)
{
//对角线
if(total>=4 && k>=2 && (k-2)<=(total-4))
{
ans = (ans + 2*c[total-4][k-2])%MOD;
}
printf("ans4 = %d\n",ans);
//一行
if(n>2 && k>=3 && total>=5 && (k-3)<=(total-5))
{
ans = (ans + 2*(n-2)*c[total-5][k-3])%MOD;
}
printf("ans5 = %d\n",ans);
//一列
if(m>2 && k>=3 && total>=5 && (k-3)<=(total-5))
{
ans = (ans + 2*(m-2)*c[total-5][k-3])%MOD;
}
printf("ans6 = %d\n",ans);
}
//只有三角放棋
if(total>=4 && k>=3 && (k-3)<=(total-4))
{
ans = (ans + 4 * c[total-4][k-3])%MOD;
}
printf("ans7 = %d\n",ans);
//四角都放棋
if(total>=4 && k>=4 && (k-4)<=(total-4))
{
ans = (ans + c[total-4][k-4])%MOD;
}
printf("ans8 = %d\n",ans);
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("../in.txt","r",stdin);
#endif
int t;
int cas = 0;
int ans;
int m,n,k;
init();
scanf(" %d",&t);
while(t--)
{
cas++;
scanf(" %d %d %d",&m,&n,&k);
ans = solve(m,n,k);
printf("Case %d: %d\n",cas,ans);
}
return 0;
}
正确的方法是使用容斥原理,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <iostream>
using namespace std;
#define MOD 1000007
#define Maxn 405
int c[Maxn+10][Maxn+10];
void init()
{
memset(c,0,sizeof(c));
for(int i=0;i<Maxn;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 solve(int m,int n,int k)
{
if(m*n<k) return 0;
int sum = 0,ans = 0;
for(int s=1;s<16;s++)
{
int row = m;
int col = n;
int t = 0;
if(s&1) row--,t++;
if(s&2) row--,t++;
if(s&4) col--,t++;
if(s&8) col--,t++;
if(t&1) sum = (sum + c[row*col][k])%MOD;
else sum = (sum - c[row*col][k] + MOD)%MOD;
}
ans = (c[m*n][k]%MOD - sum + MOD) % MOD;
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("../in.txt","r",stdin);
#endif
int t;
int cas = 0;
int ans;
int m,n,k;
init();
scanf(" %d",&t);
while(t--)
{
cas++;
scanf(" %d %d %d",&m,&n,&k);
ans = solve(m,n,k);
printf("Case %d: %d\n",cas,ans);
}
return 0;
}
另外标记一下排列组合C(i,j)的求法,可以通过C(i,j) = C(i-1,j-1) + C(i-1,j)来递推。