1 条题解
-
0
自动搬运
来自洛谷,原作者为

lg_zhou
落月摇情满江树搬运于
2025-08-24 22:32:13,当前版本为作者最后更新于2022-05-13 11:31:25,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
其实,动规都是想明白后超简单 QaQ
相比楼上的题解没用啥优化,可能实现方法不大一样。
由典型的一道题:炮兵阵地 入手。这道题很显然可以设 代表到 行为止, 行状态是 , 行状态是 ,炮兵互不冲突大最大答案。这样的复杂度是枚举阶段的 乘状态 乘决策,即枚举上面前两行的状态,复杂度为 。只是因为有每一行的合法状态很少(相邻两个一中间至少有两个零,大概 个),这样才把复杂度减少,从而通过这一题。
然而这一类问题有更普遍的一种解法,就是用更高的进制位(三进制)进行压缩,并存储更多的信息。我们规定放炮兵的位置为 , 的下面必须放 , 的下面必须放 。 的下面才可以任意放。这样我们内存可以省去一维,只用存当前第 行的状态
采用正向递推。考虑第 行填什么状态。需要满足:
- 第 行第 位为 时,第 行第 位填
- 第 行第 位为 时,第 行第 位填
- 山地不能填
- 一个格子填 后,右边两个格子不能填
因为转移比较复杂,不用写出转移方程,相邻两行进行 即可。
回到这题,我们将 和 的矩阵分别化为
和
与炮兵阵地类似,采用正向递推,需要满足如下条件:
- 第 行第 位不为 且第 行第 位已损坏,转移失败
- 第 行第 位为 时,第 行第 位填
- 第 行第 位为 时,第 行第 位填
- 已损坏的格子填零跳过
- 可以尝试填上连续三个 或者连续两个
这样这道题就做完了,我做的时候,想过一个问题,就是何时累加答案,若在填连续两个 或者连续三个 的时候累加,那么最后一行填连续两个 显然是不合法的,芯片的下半部分就超出范围了。有两种解决办法,一种是判断一下边界条件,我采用的是另一种,正常做,只不过计算答案的时候直接用 即可。
内存限制很小,滚动一下就行,改动不大。
放代码,注释很详细:
#include<iostream> #include<cstring> using namespace std; const int maxn = 155; const int maxm = 11; int a[maxn][maxm]; int T,n,m,k; int f[2][60005]; int pow[11]; void init(){ pow[0] = 1; for (int i = 1; i <= 10; i++) pow[i] = pow[i-1]*3; } int suan(int x, int pos){ // 数 x 的三进制第 pos 位是多少 return x%pow[pos]/pow[pos-1]; } bool ifok(int x, int pos, int lst){ // 第 x 行第 pos 位是否能放 if (!a[x][pos] && !suan(lst,m-pos+1)) return 1; // 当前位置不能损坏,上一行当前位置必须为零 return 0; } void dfs(int x/*这是第几行*/, int lst/*上一行的状态*/, int now/*当前行搜索到的状态*/, int pos/*从左往右搜到第三进制几位*/, int cnt/*放了几个数了*/){ if (!pos){ // 更新状态 f[x%2][now] = max(f[x%2][now],f[(x-1)%2][lst]+cnt); return; } if (suan(lst,pos)){//上一行当前非零 if (a[x][m-pos+1]) return; if (suan(lst,pos) == 2) dfs(x,lst,now*3+1,pos-1,cnt); else dfs(x,lst,now*3, pos-1,cnt); } else{ dfs(x,lst,now*3,pos-1,cnt);//什么都不放,跳! if (pos >= 2 && ifok(x,m-pos+1,lst) && ifok(x,m-pos+2,lst)){ //尝试放 3*2 的 (竖的) dfs(x,lst,(now*3+2)*3+2,pos-2,cnt+1); } if (pos >= 3 && ifok(x,m-pos+1,lst) && ifok(x,m-pos+2,lst) && ifok(x,m-pos+3,lst)){ //尝试放 2*3 的 (横的) dfs(x,lst,((now*3+1)*3+1)*3+1,pos-3,cnt+1); } } } int main(){ //freopen("a.in","r",stdin); init(); cin >>T; while (T--){ cin >> n >> m >> k; memset(a,0,sizeof a); for (int i = 1; i <= k; i++){ int x,y; cin >> x >> y; a[x][y] = 1; } memset(f,-0x3f3f3f3f,sizeof f); f[0][0] = 0; for (int i = 0; i < n; i++){ for (int j = 0; j < pow[m]; j++) f[(i+1)%2][j] = -0x3f3f3f3f; for (int j = 0; j < pow[m]; j++){ if (f[i%2][j] < 0) continue; dfs(i+1,j,0,m,0); } } cout << f[n%2][0] << endl; } return 0; }
- 1
信息
- ID
- 7025
- 时间
- 2000ms
- 内存
- 8MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者