版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/86487339
题意:
有一个n*m的矩阵,起点是(1,1),终点是(n,m)
有两个人分别从起点出发,到终点,每个人只能往下或往右走。
每一个方格只能被走一次,即一个人走过,那么另外一个人就不能走了。除了起点和终点
问他们有多少种走法?
即一个方格能画出多少对不相交的阶梯型的线(起点个终点相同)
解析:
这道题方向选错了....以为是暴力打表找规律。。。结果是一道组合数学题
之前有想过枚举方格来找方案,但是不知道怎么保证题目里往下或往右走的条件...然后就一直在找每一个方格的性质....
扫描二维码关注公众号,回复:
5018248 查看本文章
这道题枚举的其实是走法的方案。首先对于两个人,我们可以重定义起点,一个是从(1,2)->(n-1,m),另一个是从(2,1)->(n,m-1)
那么他们的走法一定是固定的——n-2次向右走+m-2次向下走。那么我们只要枚举他们这n+m-4次的排列就可以了
不考虑相交总的方案数就是
相交的情况其实我们可以转换。
每一种相交的情况都对应(1,2)->(n,m-1),(2,1)->(n-1,m)的走法的一种情况
因为一旦两个人相交,至少会有一个交点,那么我们就在他们第一个交点的时候,就让他们更换目的地,这样就成为了
一个人(1,2)->(n,m-1),一个人(2,1)->(n-1,m)的走法
这种方法在组合数学上叫插空法....
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int MAXN = 3e3+10;
const ll MOD = 1e9+7;
ll jc[MAXN];
ll inv[MAXN];
inline ll C(int a, int b) //计算C(a, b),a下,b上
{
if(b>a) return 0;
return jc[a] * inv[b] % MOD
* inv[a-b]%MOD;
}
void init()
{
ll p=1;
jc[0]=1;
jc[1]=1;
inv[0]=1;
inv[1]=1;
for(int i=2;i<MAXN;i++){
p=(p*i)%MOD;
jc[i]=p;
//inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
}
for(int i=2;i<MAXN;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD; //O(n)求逆元
for(int i=2;i<MAXN;i++) inv[i]=inv[i-1]*inv[i]%MOD; //扩展到i!的逆元
}
int main()
{
init();
int n,m;
while(~scanf("%d%d",&n,&m))
{
ll ans=C(n+m-4,n-2)*C(n+m-4,n-2)%MOD;
ll monotonous=C(n+m-4,n-3)*C(n+m-4,n-1)%MOD;
ans=(ans-monotonous+MOD)%MOD;
printf("%lld\n",ans);
}
}