AHOI2009中国象棋

首先以行为阶段,根据象棋的规则,在同一行中,至多只能有两个炮,同理:在同一列中,至多只能有两个炮
思考一个可以覆盖整个状态空间的dp数组:
dp[i]表示到了第i行
接下来我们想:某列中的炮能否通过位运算求得
我们能够发现,可能我们目前在第i行,但是在某个j行的p列有一个炮,我们要知道第i行的第p列能否放置炮。但是j可能与i相差甚远,我们不能直接通过位运算得到,逐行枚举又会耗费大量不必要的时间。
那么我们就干脆将列的状态记录在数组里,我们想我们其实并不关心到第i行时哪一列有1个炮,哪一列有两个炮,我们只需要知道到第i行时,有多少列有1个炮,有多少列有两个炮,剩下的问题我们能够通过枚举状态解决
这样就有了dp数组:
dp[i][j][k]表示到第i行时,有j列有一个炮,k列有两个炮

假设第i行只放一个炮,那么放置的方法数累加(DP方程)就是:
1.这一个炮放在了原来没有炮的位置
dp[i][j][k] += dp[i - 1][j - 1][k] * (m - j - k)
2.这一个炮放在了原来有一个炮的位置
dp[i][j][k] += dp[i - 1][j + 1][k - 1] * j

假设第i行放置了两个炮
1.这一行两个炮都放在了原来没有炮的位置
dp[i][j][k] += dp[i - 1][j - 2][k] * (m - j - k) * (m - j - k - 1) / 2;
2.这一行一个炮放在了原来有一个炮的位置,一个炮放在了原来没有炮的位置
dp[i][j][k] += dp[i - 1][j][k - 1] * (m - j - k) * j
3.这一行的两个炮都放在了原来有一个炮的位置
dp[i][j][k] += dp[i - 1][j + 2][k - 2] * j * (j - 1) / 2;

假设第i行没有放炮
dp[i][j][k] += dp[i - 1][j][k]
=-=??好像没了?接着就是处理一下每种情况能够使用的限制条件
初态:dp[0][0][0] = 1;
末态:Σdp[n][i][j]

恩是的,这是我原本的思路,但我这么写后,不知道为什么就挂了。

挂了!样例都过不了!

于是我毅然决然的把有前驱推当前状态的写法改为了由当前状态推后继状态,然后就...A了.....

方程差别不大,不做修改,直接看代码吧...

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int p = 9999973;
 5 const int maxn = 110;
 6 ll f[maxn][maxn][maxn];
 7 int n, m;
 8 
 9 inline  int read() {
10     int x = 0, y = 1;
11     char ch = getchar();
12     while(!isdigit(ch)) {
13         if(ch == '-') y = -1;
14         ch = getchar();
15     }
16     while(isdigit(ch)) {
17         x = (x << 1) + (x << 3) + ch - '0';
18         ch = getchar();
19     }
20     return x * y;
21 }
22 
23 inline int count(int k) {
24 return k * (k - 1) / 2;}
25 
26 int main() {
27     memset(f, 0, sizeof(f));
28     n = read(), m = read();
29     f[0][0][0] = 1;
30     for(int i = 0; i < n; ++i)
31         for(int j = 0; j <= m; ++j)
32             for(int k = 0; k + j <= m; ++k)
33                 if(f[i][j][k]) {
34                     f[i + 1][j][k] = (f[i][j][k] + f[i + 1][j][k]) % p;
35                     if(m - j - k >= 1) f[i + 1][j + 1][k] = (f[i + 1][j + 1][k] + f[i][j][k] * (m - j - k)) % p;
36                     if(j >= 1) f[i + 1][j - 1][k + 1] = (f[i + 1][j - 1][k + 1] + f[i][j][k] * j) % p;
37                     if(m - j - k >= 2) f[i + 1][j + 2][k] = (f[i + 1][j + 2][k] + f[i][j][k] * count(m - j - k)) % p;
38                     if(m - j - k >= 1 && j >= 1) f[i + 1][j][k + 1] = (f[i + 1][j][k + 1] + f[i][j][k] * (m - j - k) * j) % p;
39                     if(j >= 2) f[i + 1][j - 2][k + 2] = (f[i + 1][j - 2][k + 2] + f[i][j][k] * count(j)) % p;
40                     f[i][j][k] %= p;
41                 }
42     ll ans = 0;
43     for(int i = 0; i <= m; ++i)
44         for(int j = 0; j + i <= m; ++j)
45             ans = (ans + f[n][i][j]) % p;
46     cout << ans << '\n';
47     return 0;
48 } 
View Code

 

转载于:https://www.cnblogs.com/ywjblog/p/9285664.html

1. 用户与权限管理模块 角色管理: 学生:查看实验室信息、预约设备、提交耗材申请、参与安全考核 教师:管理课题组预约、审批学生耗材申请、查看本课题组使用记录 管理员:设备全生命周期管理、审核预约、耗材采购与分发、安全检查 用户操作: 登录认证:统一身份认证(对接学号 / 工号系统,模拟实现),支持密码重置 信息管理:学生 / 教师维护个人信息(联系方式、所属院系),管理员管理所有用户 权限控制:不同角色仅可见对应功能(如学生不可删除设备信息) 2. 实验室与设备管理模块 实验室信息管理: 基础信息:实验室编号、名称、位置、容纳人数、开放时间、负责人 功能分类:按学科(计算机实验室 / 电子实验室 / 化学实验室)标记,关联可开展实验类型 状态展示:实时显示当前使用人数、设备运行状态(正常 / 故障) 设备管理: 设备档案:名称、型号、规格、购置日期、单价、生产厂家、存放位置、责任人 全生命周期管理: 入库登记:管理员录入新设备信息,生成唯一资产编号 维护记录:记录维修、校准、保养信息(时间、内容、执行人) 报废处理:登记报废原因、时间,更新设备状态为 "已报废" 设备查询:支持按名称、型号、状态多条件检索,显示设备当前可用情况 3. 预约与使用模块 预约管理: 预约规则:学生可预约未来 7 天内的设备 / 实验室,单次最长 4 小时(可设置) 预约流程:选择实验室→选择设备→选择时间段→提交申请(需填写实验目的) 审核机制:普通实验自动通过,高危实验(如化学实验)需教师审核 使用记录: 签到 / 签退:到达实验室后扫码签到,离开时签退,系统自动记录实际使用时长 使用登记:填写实验内容、设备运行情况(正常 / 异常),异常情况需详细描述 违规管理:迟到 15 分钟自动取消预约,多次违规限制预约权限 4. 耗材与安全管理模块 耗材管理: 耗材档案:名称、规格、数量、存放位置、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值