• 51Nod 1486 大大走格子 —— 组合数学


    题目链接:https://vjudge.net/problem/51Nod-1486

    题目来源: CodeForces
    基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
     

    有一个h行w列的棋盘,里面有一些格子是不能走的,现在要求从左上角走到右下角的方案数。

    Input
    单组测试数据。
    第一行有三个整数h, w, n(1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000),表示棋盘的行和列,还有不能走的格子的数目。
    接下来n行描述格子,第i行有两个整数ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w),表示格子所在的行和列。
    输入保证起点和终点不会有不能走的格子。
    Output
    输出答案对1000000007取余的结果。
    Input示例
    3 4 2
    2 2
    2 3
    Output示例
    2
    System Message (题目提供者)

    题意:

    从(1,1)走到(h,w),只能往下或者往右,且不能走障碍点,问有多少条路线?

    题解:

    1.可知从(0,0)走到(n,m)有 C(n+m,n)条路径,那么对于一个位于(x,y)的障碍,从(1,1)走到它的位置有C(x+y-2,x-1)条路径。

    2.设dp[i]为从(1,1)走到障碍i,且途中不经过任何障碍(除自己外)的路径数。那么怎么求dp[i]呢?

    2.1 首先不考虑途中的障碍,那么就有C(x+y-2,x-1)条路径。

    2.2 然后再考虑回途中的障碍,如果障碍j满足:x[j]<=x[i] && y[j]<=y[i],那么它就会存在于某些路径当中,即这些路径不合法,需要去除。为了把从(1,1)到障碍i所有不合法的路径删除掉,既不多删也不漏删,就需要一种合理的删除方式:对于一个满足x[j]<=x[i] && y[j]<=y[i] 的障碍j,我们删除以障碍j为路径上第一个障碍的路径,即 dp[j]*C(x[i]-x[j]+y[i]-y[j]-2, x[i]-x[j]-1),然后枚举所有满足条件的障碍j,就正好能够不多不少地把非法路径去除掉。

    3. 为了操作方便,把右下角也当成是一个障碍,那么得到的dp[]即为答案。

    学习之处:

    1.求C[n][m]可以不用O(nm)的时间、空间去预处理,也可以不用O(m)的时间根据公式 C[n][m] = (n-m+1)*C[n][m-1]/m 进行递推。只需先用O(n)的时间预处理出阶乘A[],然后再利用公式:C[n][m] = A[n]/((n-m)!*m!) 以O(1)的时间复杂度求出。

    2.按一定的规则或限定去枚举一个合法(非法)对象,并求出其对答案的影响,那么所有合法(非法)对象对答案的影响的并集,即为答案。

    代码如下:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <stack>
     9 #include <map>
    10 #include <string>
    11 #include <set>
    12 using namespace std;
    13 typedef long long LL;
    14 const int INF = 2e9;
    15 const LL LNF = 9e18;
    16 const int MOD = 1e9+7;
    17 const int MAXN = 2e3+10;
    18 
    19 struct node
    20 {
    21     int x, y;
    22     bool operator<(const node &a){
    23         if(x==a.x) return y<a.y;
    24         return x<a.x;
    25     }
    26 }a[MAXN];
    27 
    28 LL qpow(LL x, LL y)
    29 {
    30     LL s = 1;
    31     while(y)
    32     {
    33         if(y&1) s = (s*x)%MOD;
    34         x = (x*x)%MOD;
    35         y >>= 1;
    36     }
    37     return s;
    38 }
    39 
    40 LL A[200010], inv[200010];  //预处理出阶乘A[i],以及逆元inv[i],其中inv[i]是A[i]的逆元。
    41 LL C(int n, int m)
    42 {
    43     return (((A[n]*inv[n-m])%MOD)*inv[m])%MOD;
    44 }
    45 
    46 LL dp[MAXN];
    47 int main()
    48 {
    49     int h, w, n;
    50     A[0] = inv[0] = 1;
    51     for(int i = 1; i<200010; i++)   //预处理
    52     {
    53         A[i] = (i*A[i-1])%MOD;
    54         inv[i] = qpow(A[i], MOD-2);
    55     }
    56     while(scanf("%d%d%d", &h,&w,&n)!=EOF)
    57     {
    58         for(int i = 1; i<=n; i++)
    59             scanf("%d%d", &a[i].x, &a[i].y);
    60         a[++n].x = h; a[n].y = w;   //把右下角加进去,简化处理
    61         sort(a+1,a+1+n);    //按坐标排序
    62         for(int i = 1; i<=n; i++)
    63         {
    64             dp[i] = C(a[i].x+a[i].y-2, a[i].x-1);   //初始化
    65             for(int j = 1; j<i; j++)    //去除不合法的
    66             if(a[i].x>=a[j].x&&a[i].y>=a[j].y)
    67                 dp[i] = (dp[i]-(dp[j]*C(a[i].x-a[j].x+a[i].y-a[j].y, a[i].x-a[j].x))%MOD+MOD)%MOD;
    68         }
    69         printf("%lld
    ", dp[n]);
    70     }
    71 }
    View Code
  • 相关阅读:
    推荐.Net、C# 逆向反编译四大工具利器
    逆向工具/反编译工具 集合
    秒杀系统-并发处理
    react动态渲染组件
    通过dom获取react节点
    mac上安装windows触摸板不能右键
    STM32F103智能配网mqtt协议EMQ安卓App远程控制LED采集温湿度ESP8266
    itop4412 基于物联网技术的商品支付系统 毕业设计
    STM32F103 单片机最小系统 核心板 C8T6 MINI 飞控 小车 主控制板
    31865 MAX31865 RTD铂电阻温度检测器 PT100至PT1000传感器模块
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8689196.html
Copyright © 2020-2023  润新知