Pieces Assignment(状态DP)

本文探讨了一个关于n*m棋盘布局的问题,目标是在棋盘上放置k个棋子,确保任意两个棋子不相邻。通过状态DP算法解决此问题,详细介绍了二进制表示方法、状态转移方程及实现细节。

Pieces Assignment

  Source : zhouguyue
  Time limit : 1 sec  Memory limit : 64 M

Submitted : 267, Accepted : 107

 

 

Background

 

    有一个n*m的棋盘(n、m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻(每个棋子最多和周围4个棋子相邻)。求合法的方案总数。

 

Input

 

    本题有多组测试数据,每组输入包含三个正整数n,m和k。

 

Output

 

    对于每组输入,输出只有一个正整数,即合法的方案数。

 

Sample Input

 

 

2 2 3
4 4 1

 

 

Sample Output

 

 

0
16

 

       思路:

       状态 DP。以行为标准,因为 n * m <= 80,说明因为 8 * 8 = 81 > 80,所以行列不可能同时大于或者等于 8,行列至少有一个是小于8的,故考虑的时候始终让列数小于 8,故一行有无放棋子的可能是 2 ^ 8,因为不要求相邻,所以可以通过一组二进制来表示一行可能存在的棋子的情况。比如该棋盘大小为 4 X 3,那么说明一行可能出现的情况是 000 , 001 , 010 ,100 , 101  5 种情况。而每个二进制数都可以用一个十进制数来表示。用 s 数组记录每个可能情况的二进制所对应的十进制数,用 c 数组记录每个可能情况的二进制所对应的 1 的个数。预处理这些序列之后,就可以递推下去了。

       用 dp [ i ] [ j ] [ k ],代表第 i 行时,运用 j 这个二进制的放置方法,所构成到这行为止总共的棋子数为 k 的情况总数。那么:

       dp [ i ] [ j ] [ k ] += dp [ i - 1 ] [ a ] [ k - c[ j ] ] (当满足 s [ j ] & s [ a ] == 0 时),最后循环所有 way 的 dp [ n ] [ way ] [ k ] 求得总和即为答案。数组要开 long long,得出的结果也要用 long long 保存,因为情况会爆 int。

 

       AC:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

int n, m, k, way;
int c[600], s[600];
ll dp[100][600][25];

void make_num() {

        for (int i = 0; i < (1 << m); ++i) {
                int num = i, one = 0;
                int temp = 0, last = num % 2;
                num /= 2;
                if (last) ++one;

                while (num) {
                        if (num % 2) ++one;
                        if (last && (num % 2)) {
                                temp = 1;
                                break;
                        }
                        last = num % 2;
                        num /= 2;
                }

                if (temp) continue;
                else {
                        ++way;
                        c[way] = one;
                        s[way] = i;
                }
        }

}

void solve() {

        memset(dp, 0, sizeof(dp));

        dp[0][1][0] = 1;

        for (int i = 1; i <= n; ++i) {
                for (int kk = 0; kk <= k; ++kk) {
                        for (int p = 1; p <= way; ++p) {
                                if (c[p] > kk) continue;

                                for (int q = 1; q <= way; ++q) {

                                        if (!(s[p] & s[q])) {
                                            dp[i][p][kk] +=
                                            dp[i - 1][q][kk - c[p]];
                                        }
                                }
                        }
                }
        }

        ll res = 0;
        for (int i = 1; i <= way; ++i) {
                res += dp[n][i][k];
        }

        printf("%lld\n", res);

}

int main() {

        while (~scanf("%d%d%d", &n, &m, &k)) {
                if (n < m) swap(n, m);

                way = 0;

                make_num();

                solve();

        }

        return 0;
}

 

 

基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值