[51Nod1371]填数字(DP)

  题目传送门

分析

  考虑DP, f[i][j][k] f [ i ] [ j ] [ k ] 表示前 i i 行里有j列可以填 1 1 ,有k列可以填 2 2 ,然后我们有7种转移:
(1)什么都不填,有 1 1 种方法,即f[i+1][j][k+1]+=f[i][j][k]
(2)在前面几列中某一列填 2 2 ,有k+1种方法,即 f[i+1][j][k]+=(k+1)f[i][j][k] f [ i + 1 ] [ j ] [ k ] + = ( k + 1 ) ∗ f [ i ] [ j ] [ k ]
(3)在前面几列中某一列原来可以填 2 2 的列上填1,有 k+1 k + 1 种方法,即 f[i+1][j+1][k]+=(k+1)f[i][j][k] f [ i + 1 ] [ j + 1 ] [ k ] + = ( k + 1 ) ∗ f [ i ] [ j ] [ k ]
(4)在前面几列中某一列原来可以填 1 1 的列上填1,有 j j 种方法,即f[i+1][j1][k]+=jf[i][j][k](j>0)
(5)在前面几列中某两列原来可以填 2 2 的列上填1,有 (k+1)k/2 ( k + 1 ) ∗ k / 2 种方法,即 f[i+1][j+2][k1]+=(k(k+1)/2)f[i][j][k](k>0) f [ i + 1 ] [ j + 2 ] [ k − 1 ] + = ( k ∗ ( k + 1 ) / 2 ) ∗ f [ i ] [ j ] [ k ] ( k > 0 )
(6)在前面几列中某两列原来可以填 1 1 的列上填1,有 j(j1)/2 j ∗ ( j − 1 ) / 2 种方法,即 f[i+1][j2][k+1]+=(j(j1)/2)f[i][j][k](j>1) f [ i + 1 ] [ j − 2 ] [ k + 1 ] + = ( j ∗ ( j − 1 ) / 2 ) ∗ f [ i ] [ j ] [ k ] ( j > 1 )
(7)分别在一列原来可以填 2 2 的列和原来可以填1的列上填 1 1 ,有j(k+1)种方法,即 f[i+1][j][k]+=(j(k+1))f[i][j][k] f [ i + 1 ] [ j ] [ k ] + = ( j ∗ ( k + 1 ) ) ∗ f [ i ] [ j ] [ k ]
  最后答案就是 i=0nj=0nf[n][i][j](i+j<=n) ∑ i = 0 n ∑ j = 0 n f [ n ] [ i ] [ j ] ( i + j <= n )

Code

#include<bits/stdc++.h>
using namespace std;
const int mod=1e8+7;
namespace {
    inline int Half(const int &x) {
        return x&1?x+mod>>1:x>>1;
    }
    inline int Add(const int &x,const int &y) {
        int res=x+y;
        return res>=mod?res-mod:res;
    }
    inline int Sub(const int &x,const int &y) {
        int res=x-y;
        return res<0?res+mod:res;
    }
    inline int Mul(const int &x,const int &y) {
        return 1ll*x*y%mod;
    }
    inline int C_2(const int &x) {
        return Half(Mul(x,x-1));
    }
}
int f[205][205][205],n;
int main() {
    cin>>n;
    f[0][0][0]=1;
    for(int i=0;i<n;++i)
        for(int j=0;j<=i;++j)
            for(int k=0;k<=i;++k)
                if(j+k<=i) {
                    /*Tramsform place Nothing:*/ {
                        f[i+1][j][k+1]=Add(f[i+1][j][k+1],f[i][j][k]);
                    }
                    /*Transform Place a two:*/ {
                        f[i+1][j][k]=Add(f[i+1][j][k],Mul(k+1,f[i][j][k]));
                    }
                    /*Transform Place a one in k*/ {
                        f[i+1][j+1][k]=Add(f[i+1][j+1][k],Mul(k+1,f[i][j][k]));
                    }
                    /*Tramsform Place a one in j*/ {
                        if(j)
                            f[i+1][j-1][k+1]=Add(f[i+1][j-1][k+1],Mul(j,f[i][j][k]));
                    }
                    /*Transform Place two one in j*/ {
                        if(j>1)
                            f[i+1][j-2][k+1]=Add(f[i+1][j-2][k+1],Mul((1ll*(j-1)*j/2)%mod,f[i][j][k]));
                    }
                    /*Transform Place a one in j and a one in k*/ {
                        if(j)
                            f[i+1][j][k]=Add(f[i+1][j][k],Mul(Mul(j,k+1),f[i][j][k]));
                    }
                    /*Transform Place two one in k*/ {
                        if(k)
                            f[i+1][j+2][k-1]=Add(f[i+1][j+2][k-1],Mul((1ll*(k+1)*k/2)%mod,f[i][j][k]));
                    }
                }
    int ans=0;
    for(int j=0;j<=n;++j)
        for(int k=0;k<=n;++k)
            if(j+k<=n)
                ans=Add(ans,f[n][j][k]);
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值