51nod1327 棋盘游戏 [dp]

本文探讨了一个特定的棋盘布局问题,目标是在满足一系列约束条件下计算不同的棋子放置方案数量。通过动态规划的方法解决了该问题,并给出了详细的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description:
有一个NNM列的棋盘,即该棋盘被分为NMN∗M格。现在向棋盘中放棋子,每个格子中最多放一个棋子,也可以一个不放。放完棋子后需要满足如下要求:
11)对于第i行来说,其从左往右的前left[i]left[i] 个格子(即最左侧的left[i]left[i] 个连续的格子)中恰好一共有11个棋子;
2对于第i行来说,其从右往左的前right[i]right[i]个格子(即最右侧的right[i]right[i]个连续的格子)中恰好一共有11个棋子;
3对于每一列来说,这一列上的所有格子内含有的棋子数不得超过11个
其中,11)22)条件中,对所有ii满足left[i]+right[i]<=M,即两个区间不会相交。
问,符合上述条件情况下棋子的不同放法一共有多少种?输出放法的个数 mod1,000,000,007mod1,000,000,007后的结果。


Solution:
由于同时有leftleftrightright,不能同时进行dpdp,否则复杂度爆炸。那么考虑满足所有leftleft的方案,那么在每个leftleft结束前都要选完。设dp[i][j][k]dp[i][j][k]表示选到第ii列,有j列空着,还有kkright没选。先计算每列有多少个leftleft结束点,rightright起始点,和空着的点。那么考虑加入新的一列,这列里的leftleft结束点必须选完,那么就是在空着的列中放置这些leftleft。乘上排列即可。新加入的列也可以放置其他两种格子,分别计算即可。


#include <bits/stdc++.h>
using namespace std;
const int maxn = 205, P = 1e9 + 7;
int n, m;
long long fac[maxn], C[maxn][maxn], a[maxn], b[maxn], c[maxn], l[maxn], r[maxn], dp[maxn][maxn][maxn];
void upd(long long &x, long long y) {
    x = (x + y) % P; 
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%d%d", &l[i], &r[i]);
        for(int j = 1; j <= m; ++j) {
            if(j > l[i] && j <= m - r[i]) {
                ++a[j];
            }
            if(j == l[i]) {
                ++b[j];
            }
            if(j == m - r[i] + 1) {
                ++c[j];
            }
        }
    }
    C[0][0] = 1;
    for(int i = 1; i <= m; ++i) {
        C[i][0] = 1;
        for(int j = 1; j <= i; ++j) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % P;
        }
    }
    fac[0] = 1;
    for(int i = 1; i <= m; ++i) {
        fac[i] = 1LL * fac[i - 1] * i % P;
    }
    dp[0][0][0] = 1;
    for(int i = 0; i < m; ++i) {
        for(int j = 0; j <= i; ++j) {
            for(int k = 0; k <= n; ++k) {
                if(dp[i][j][k]) {
                    if(j + 1 >= b[i + 1]) {
                        upd(dp[i + 1][j + 1 - b[i + 1]][k + c[i + 1]], dp[i][j][k] * C[j + 1][b[i + 1]] % P * fac[b[i + 1]] % P);               
                    } 
                    if(j >= b[i + 1]) {
                        upd(dp[i + 1][j - b[i + 1]][k + c[i + 1]], dp[i][j][k] * a[i + 1] % P * C[j][b[i + 1]] % P * fac[b[i + 1]] % P);
                        if(k + c[i + 1]) {
                            upd(dp[i + 1][j - b[i + 1]][k + c[i + 1] - 1], dp[i][j][k] * (k + c[i + 1]) % P * C[j][b[i + 1]] % P * fac[b[i + 1]] % P);
                        }
                    }
                }
            }
        }
    }
    long long ans = 0;
    for(int i = 0; i <= m; ++i) {
        upd(ans, dp[m][i][0]);
    } 
    printf("%lld\n", ans); 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值