【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场
【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场
题目链接跳转:点击跳转
前置知识:
- 了解过基本的动态规划。
- 熟练掌握二进制的位运算。
题解思路
这是一道典型的状压动态规划问题。设
对于每一行的方案,我们可以用一个二进制来表示。例如二进制数字
首先,我们预处理出横排所有放置乌龟的合法情况。根据题意,两个乌龟不能相邻放置,因此在二进制中,不能有两个
如果存在一个二进制数字有两个 (x << 1) & x
的结果或 (x >> 1) & x
的结果必定大于等于
接下来如何保证上下两行的乌龟不冲突?假如上一行的摆放状态是 i & j
的结果大于等于
综上所述,我们可以得出状态转移方程:
状态的初始化也很简单,另
时间复杂度
通过观察上述代码,在枚举所有状态和转移状态的时候有三层循环,分别是枚举当前行、枚举当前行的合法摆放情况以及枚举上一行的摆放情况。因此总时间复杂度约为
代码实现
本题的代码实现如下。在输出的时候需要减一,因为不放置也是一种合法情况,根据题目要求需要把这一合法情况排除。
#include <iostream>
using namespace std;
const int MOD = 1e9+7;
int n, m, ans;
int arr[505][505];
// 所有横排合法的情况。
int terrain[505];
int ok[1050], cnt;
int dp[505][1050];
int main(){
cin >> n >> m;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
cin >> arr[i][j];
}
}
// 预处理非法地形。
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
terrain[i] = (terrain[i] << 1) + !arr[i][j];
}
}
// 预处理出所有横排的合法情况。
for (int i=0; i<(1<<m); i++){
if (((i<<1)|(i>>1)) & i) continue;
ok[++cnt] = i;
}
dp[0][1] = 1;
// 枚举。
for (int i=1; i<=n; i++){
for (int s1=1; s1<=cnt; s1++){ // 枚举当前行。
if (ok[s1] & terrain[i]) continue;
for (int s2=1; s2<=cnt; s2++){ // 枚举上一行。
if (ok[s2] & terrain[i-1]) continue;
if (ok[s1] & ok[s2]) continue;
dp[i][s1] = (dp[i][s1] + dp[i-1][s2]) % MOD;
}
}
}
// 统计答案。
int ans = 0;
for (int i=1; i<=cnt; i++)
ans = (ans + dp[n][i]) % MOD;
cout << ans - 1 << endl;
return 0;
}
本题的 Python 代码如下,Python 可以通过本题的所有测试点:
MOD = int(1e9 + 7)
n, m, ans = 0, 0, 0
arr = [[0] * 505 for _ in range(505)]
terrain = [0] * 505
ok = [0] * 1050
dp = [[0] * 1050 for _ in range(505)]
cnt = 0
def main():
global n, m, cnt, ans
# 输入 n 和 m
n, m = map(int, input().split())
# 输入 arr 数组
for i in range(1, n + 1):
arr[i][1:m + 1] = map(int, input().split())
# 预处理非法地形
for i in range(1, n + 1):
for j in range(1, m + 1):
terrain[i] = (terrain[i] << 1) + (1 - arr[i][j])
# 预处理出所有横排的合法情况
for i in range(1 << m):
if ((i << 1) | (i >> 1)) & i:
continue
cnt += 1
ok[cnt] = i
dp[0][1] = 1
# 枚举
for i in range(1, n + 1):
for s1 in range(1, cnt + 1): # 枚举当前行
if ok[s1] & terrain[i]:
continue
for s2 in range(1, cnt + 1): # 枚举上一行
if ok[s2] & terrain[i - 1]:
continue
if ok[s1] & ok[s2]:
continue
dp[i][s1] = (dp[i][s1] + dp[i - 1][s2]) % MOD
# 统计答案
ans = 0
for i in range(1, cnt + 1):
ans = (ans + dp[n][i]) % MOD
print(ans - 1)
if __name__ == "__main__":
main()
再提供一个暴力解法用于对拍:
#include <iostream>
using namespace std;
const int MOD = 1e9+7;
int n, m, ans;
int arr[505][505];
int dx[] = {0, 1, -1, 0};
int dy[] = {1, 0, 0, -1};
// 深度优先搜索 Brute Force
void dfs(int x, int y){
if (x > n) {
ans += 1;
ans %= MOD;
return ;
}
if (y > m){
dfs(x+1, 1);
return ;
}
if (arr[x][y] == 0){
dfs(x, y+1);
return ;
}
// 不放鱼
dfs(x, y+1);
// 放鱼
for (int i=0; i<4; i++){
int cx = x + dx[i];
int cy = y + dy[i];
if (cx < 1 || cy < 1 || cx > n || cy > m) continue;
if (arr[cx][cy] == 2) return ;
}
arr[x][y] = 2;
dfs(x, y+1);
arr[x][y] = 1;
return ;
}
int main(){
cin >> n >> m;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
cin >> arr[i][j];
}
}
// dfs 暴力
dfs(1, 1);
cout << ans-1 << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程