关于在矩阵中枚举点的 dp

关于在矩阵中枚举点的 dp

JOISC 2018 Day1 camp

简要题意:

在矩阵中放一个点,每个点所在的行与列至多有一个别的点,且若有点,两个点的方向要相对,而若无点,则可任意指向四个方向。

考虑 dp 。

首先是 70 70 70 分的思路。令 d p i , j , k dp_{i,j,k} dpi,j,k 表示到第 i i i 行,此时还剩 j j j 个空列,剩 k k k 个只能放一个的列(即此列中上方已经有一个点方向向下,次格放的点只能方向向上),称之为半列。

  • d p i , j , k + = d p i − 1 , j + 2 , k × C j + 2 2 dp_{i,j,k}+=dp_{i-1,j+2,k}\times C_{j+2}^2 dpi,j,k+=dpi−1,j+2,k×Cj+22
  • d p i , j , k + = d p i − 1 , j + 1 , k − 1 × ( j + 1 ) dp_{i,j,k}+=dp_{i-1,j+1,k-1}\times(j+1) dpi,j,k+=dpi−1,j+1,k−1×(j+1)
  • d p i , j , k + = d p i − 1 , j , k + 1 × ( k + 1 ) dp_{i,j,k}+=dp_{i-1,j,k+1}\times(k+1) dpi,j,k+=dpi−1,j,k+1×(k+1)
  • d p i , j , k + = d p i − 1 , j + 1 , k × ( j + 1 ) × 3 dp_{i,j,k}+=dp_{i-1,j+1,k}\times(j+1)\times3 dpi,j,k+=dpi−1,j+1,k×(j+1)×3

第一个方程表示选择两个空列;第二个方程表示,选择一个空列,令点的方向向下,形成一个半列;第三个方程表示选择一个半列,让这一列填满;第四个方程表示选择一个空列,放的点的方向指向左右上(若向下则会形成一个半列)。

然后是 100 100 100 分的思路。

令 d p i , j dp_{i,j} dpi,j 表示把 i × j i\times j i×j 的矩阵填满,可能得方案数。转移方程:

  • d p i , j + = d p i − 1 , j − 1 × 4 × j dp_{i,j}+=dp_{i-1,j-1}\times4\times j dpi,j+=dpi−1,j−1×4×j
  • d p i , j + = d p i − 2 , j − 1 × ( i − 1 ) × j dp_{i,j}+=dp_{i-2,j-1}\times(i-1)\times j dpi,j+=dpi−2,j−1×(i−1)×j
  • d p i , j + = d p i − 1 , j − 2 × C j 2 dp_{i,j}+=dp_{i-1,j-2}\times C_{j}^{2} dpi,j+=dpi−1,j−2×Cj2

第一个方程表示将矩阵拓展一行一列,由于新的一行一列是空列,有四个方向,我们只考虑向下拓展行,而可以在任意位置拓展列,在行列交点处放一个点,由于行列上没有其他点,故有四个方向;第二个方程表示拓展两行一列,每行放置一个,让两个格子的方向相对,使那个列填满,其中一行必然在最底下,而另一行则可以选择位置插入;第三个方程表示拓展一行两列,让两个格子在同一行,让这一行填满,任意插入两列。最后计算时由于行列之间可以任意插入空的行列,故 a n s = ∑ i = 0 n ∑ j = 0 m d p n − i , j − m × C n i × C m j − 1 \displaystyle ans=\sum_{i=0}^{n}\sum_{j=0}^m dp_{n-i,j-m}\times C_{n}^i\times C_{m}^j-1 ans=i=0∑nj=0∑mdpn−i,j−m×Cni×Cmj−1 (减去一是为了减去一个格子都不放的情况)。

总体来说对于行列,只有两种情况:放了两个,并且相对;放了一个,可以朝四个方向。通过不断添行列巧妙避免重复,同时较为快速算出答案。

Code:

cpp 复制代码
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int N=3010;
long long dp[N][N],ans;
long long jc[N+10],rejc[N+10];
int n,m;
long long ksm(long long a,long long b){
    long long res=1;
    while(b){
        if(b&1) res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
long long c(int x,int y){
    return jc[x]*rejc[y]%mod*rejc[x-y]%mod;
}
void update(long long&x,long long y){
    x+=y;
    if(x>=mod) x-=mod;
}
int main(){
    scanf("%d%d",&n,&m);
    jc[0]=1;
    for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;
    rejc[N]=ksm(jc[N],mod-2);
    for(int i=N-1;i>=0;i--) rejc[i]=rejc[i+1]*(i+1)%mod;
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            dp[i][j]=dp[i-1][j-1]*j*4%mod;
            if(i>=2) update(dp[i][j],dp[i-2][j-1]*(i-1)*j%mod);
            if(j>=2) update(dp[i][j],dp[i-1][j-2]*c(j,2)%mod);
        }
    }
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++) update(ans,dp[n-i][m-j]*c(n,i)%mod*c(m,j)%mod);
    }
    printf("%lld",ans-1);
    return 0;
}
相关推荐
糕冷小美n7 分钟前
jeecgbootvue2重新整理数组数据或者添加合并数组并遍历背景图片或者背景颜色
数据结构·算法
CV万花筒12 分钟前
点云欧式聚类,条件欧式聚类算法原理及推导
算法·数据挖掘·聚类
ZZZ_O^O38 分钟前
【贪心算法-第三弹——Leetcode-179.最大数】
c++·学习·算法·leetcode·贪心算法
Matlab程序猿小助手1 小时前
【MATLAB源码-第228期】基于matlab的鼠群优化算法(RSO)无人机三维路径规划,输出做短路径图和适应度曲线.
开发语言·算法·matlab·机器人·无人机
z千鑫2 小时前
【C/C++】深入解析 Stack 与 Queue 数据结构(详解):实现原理、应用场景与性能优化
c语言·开发语言·数据结构·c++·深度学习·算法·排序算法
Y编程小白2 小时前
Leecode经典题2--移除元素
数据结构·算法·leetcode
Wils0nEdwards2 小时前
Leetcode 颠倒二进制位
算法·leetcode·职场和发展
醇醛酸醚酮酯2 小时前
两数之和--leetcode100题
数据结构·算法·leetcode
xiaoshiguang32 小时前
LeetCode:19.删除链表倒数第N个节点
算法·leetcode·链表
m0_675988232 小时前
Leetcode3208:交替组 II
c语言·数据结构·算法·leetcode