算法设计与分析 第四次编程作业 1163. 棋盘

该博客介绍了如何使用位运算和动态规划算法解决一个棋盘问题,具体是计算在给定的破损棋盘上放置马的方案数。题目中给出了输入输出格式以及数据范围,并展示了一段C++代码实现。博主强调了代码中位运算的巧妙应用,并对代码思路进行了详细解释。最后,博主感谢了rxygg的启发。

题目描述

给定一个大小为m×nm \times nm×n的棋盘,其中有一些格子是破损的。破损的格子不能摆放棋子,但是不影响棋子移动。请求出摆放马(数量不限)使得他们不会互相攻击的方案数。 这里马的攻击范围是日字形(参考国际象棋规则)。

输入格式

输入第一行三个整数m,n,km,n,km,n,k, mmmnnn表示棋盘的大小,kkk表示破碎格子的个数。
接下来kkk行,每行两个整数表示破损格子的位置。

输出格式

输出一个整数,表示方案数。由于方案可能过多,请输出对(109+7)(10^9+7)(109+7)取模的值。

样例输入

3 3 1
1 1

样例输出

68

数据范围

对于 100%的数据,保证1≤m≤2001 \leq m \leq 2001m2001≤n≤61 \leq n \leq 61n6

解决方案

#include <iostream> 
using namespace std; 
const int MOD = 1e9 + 7; 
int dp[222][65][65]; 
int broken[222]={0}; 
//dp[i][j][k]: i is the row we choose now, j is the state of the i'th row, k is the state of the (i-1)'th row 
//本题参考了rxygg的代码,我在充分理解了他的思路以后借鉴了他的写法,感谢!
//本题的主要思路是,用二进制来表示每一行的棋子分布,由于限制了1<=n<=6,这个方法不会溢出.
//
int main(int argc, char *argv[]) 
{ 
    int m, n, k; 
    scanf("%d %d %d", &m, &n, &k); 
    for (int i = 0; i < k; i++) 
    { 
        int x, y; 
        scanf("%d %d", &x, &y); 
        broken[x] += 1 << (y - 1); //用位来存储破损的棋子位置
    } 
    for (int i = 2; i <= m; i++) //遍历每一行
    { 
        for (int j = 0; j < (1 << n); j++) //j是当前行的排列组合方式,例如对于n=5,j可以是二进制的00000~11111,代表某一位有无棋子
            { 
                bool flag1=j & broken[i]; 
                if (flag1) 
                    { 
                        continue; 
                    } 
                for (int k = 0; k < (1 << n); k++) { //k是上一行的棋子分布
                    bool flag2=(k << 2) & j || (k >> 2) & j || k & broken[i - 1]; //上一行的马对我的影响
                    if (!flag2) { 
                        if (i == 2) { //没有再上一行的马影响
                            dp[2][j][k] = 1; 
                        } 
                        else { 
                            for (int p = 0; p < (1 << n); p++) { //p指代再上一行的棋子分布
                            bool flag3=(p << 1) & j || (p >> 1) & j || broken[i - 2] & p; //再上一行的马对我的影响
                                if (!flag3) { 
                                    dp[i][j][k] = (dp[i][j][k] + dp[i - 1][k][p]) % MOD; //对于第i行第j列的格子,用一个数组dp[i][i]去存上一行的各种情况下,这一行具有的种类数.
                                } 
                            } 
                        } 
                    } 
                } 
            } 
    } 
    int output = 0; 
    for (int i = 0; i < (1 << n); i++) { 
        for (int j = 0; j < (1 << n); j++) { 
            output = (output + dp[m][i][j]) % MOD; 
        } 
    } 
    printf("%d", output); 
    return 0;
} 

总结

本题是在rxygg的提示下得到的,这题非常巧妙地使用了位运算来表征各行的棋子分布情况,堪称位运算运用的经典。

声明

本文的题目版权属于TA、代码思路和代码均为rxygg的智慧结晶,仅作学习和分享,如有侵权,请联系删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值