hihocoder 1124 : 好矩阵 dp

32 篇文章 0 订阅

好矩阵

时间限制: 3000ms
单点时限: 1000ms
内存限制: 256MB

描述

给定n, m。一个n × m矩阵是好矩阵当且仅当它的每个位置都是非负整数,且每行每列的和 ≤ 2。求好矩阵的个数,模109 + 7

输入

第一行一个整数T,表示测试点个数。下面T个测试点。每个测试点一行,包含两个整数n,m。1 ≤ T ≤ 104. 1 ≤ n, m ≤ 100.

输出

T行。每行是对应测试点的答案。

样例输入
1
2 2
样例输出
26

题意很简单,由于,数量很大,如果考虑一个一个方格的放,要考虑横向的,又要考虑竖向的,很复杂,所以不可取。所以一排一排的放,如果,m是一定的,那么,每一排只需要考虑不超过2,且与前面已经排好的不冲突就可以了。

dp[i][a][b]表示,第i排,有a列0,b列1,m - a - b列2,的个数则

dp[i+1][a][b]+= dp[i][a][b];//第i+1排全放0
dp[i+1][a-1][b] += (ll)a * dp[i][a][b];//第i+1排在和为0那些列放一个2
dp[i+1][a-1][b+1] += (ll)a * dp[i][a][b];//第i+1排和为1放一个1
dp[i+1][a-1][b]+= (ll)a * (ll) b * dp[i][a][b];//第i+1排和为0 和为1的列各选一个 放两个1
dp[i+1][a][b-1] += (ll)b * dp[i][a][b];//第i+1排选一个和为1的列放一个1
dp[i+1][a-2][b+2] += (ll)(a * (a-1)/2) * dp[i][a][b];//第i+1排选两个和为0放两个1
dp[i+1][a][b-2] += (ll)(b * (b-1)/2) * dp[i][a][b];//第i+1排选两个和为1的放两个1

总复杂度为o(n^4);

#define N 105
#define M 100005
#define maxn 205
#define MOD 1000000007
int n,m,T;
ll dp[N][N][N],ans[N][N];
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    for(int m = 1;m<=100;m++){
        int n = 100;
        for(int i =0;i<=n;i++){
            for(int a = 0;a<=m;a++){
                for(int b = 0;b<=m;b++){
                    dp[i][a][b] = 0;
                }
            }
        }
        dp[0][m][0] = 1;
        for(int i = 0;i<n;i++){
            for(int a = 0;a<=m;a++){
                for(int b = 0;a + b<=m;b++){
                    dp[i+1][a][b]+= dp[i][a][b];
                    dp[i+1][a][b] %= MOD;
                    if(a >= 1){
                        dp[i+1][a-1][b] += (ll)a * dp[i][a][b];
                        dp[i+1][a-1][b] %= MOD;
                        dp[i+1][a-1][b+1] += (ll)a * dp[i][a][b];
                        dp[i+1][a-1][b+1] %= MOD;
                    }
                    if(a >= 1 && b >= 1){
                        dp[i+1][a-1][b]+= (ll)a * (ll) b * dp[i][a][b];
                        dp[i+1][a-1][b] %= MOD;
                    }
                    if(b >= 1 ){
                        dp[i+1][a][b-1] += (ll)b * dp[i][a][b];
                        dp[i+1][a][b-1] %= MOD;
                    }
                    if(a >= 2 ){
                        dp[i+1][a-2][b+2] += (ll)(a * (a-1)/2) * dp[i][a][b];
                        dp[i+1][a-2][b+2] %= MOD;
                    }
                    if(b >= 2 ){
                        dp[i+1][a][b-2] += (ll)(b * (b-1)/2) * dp[i][a][b];
                        dp[i+1][a][b-2] %= MOD;
                    }
                }
            }
            ans[i+1][m] = 0;
            for(int a = 0;a<=m;a++){
                for(int b = 0;b<=m;b++){
                    ans[i+1][m] += dp[i+1][a][b];
                    ans[i+1][m] %= MOD;
                }
            }
        }
    }
    while(S(T)!=EOF)
    {
        while(T--){
            int s,e;
            S2(s,e);
            printf("%lld\n",ans[s][e]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值