DP(状压专题十三)

题意: 这题就是传说中的炮兵阵地 洛谷P2704, 题意很简单, 在一个 m × n ( 1 ≤ m ≤ 100 , 1 ≤ n ≤ 10 ) m\times n( 1\leq m \leq100, 1\leq n\leq10) m×n(1m100,1n10)的矩阵里面放十字架, 要求十字架的中心不被覆盖, 矩阵为’P’的地方才可以放十字中心, 问最多能放多少个十字架

>> face <<

Strategy:状压DP, i是一个表示二进制的十进制数,'1’该位置已经放置了一个十字中心,代表当前行的状态

状态: 看到题后会比较自然的想出dp[i][j][k]代表目前第i行状态为j,上一行状态为k所能放置的最大十字架数量, 但是很容易会发现dp[101][1<<10][1<<10]会很大, (101 * 1024 *1024 *4 / 1024 /1024 = 404mb, 题目只给了128mb). 一番思索就会发现,对当前行有用的数据只有上一行和上上一行, 于是就可以设计成dp[3][1 << 10][1 << 10]的滚动数组

目标: d p [ m % 3 ] [ i ] [ j ] ∣ m a x dp[m\%3][i][j]\mid_{max} dp[m%3][i][j]max最后一行的任意状态和上一行的任意状态的最大值

边界: 如果从第三行推至少需要前两行的信息, 所以边界就是初始化第一行和第二行, 显然,初始化第一行就用用任意合法状态填充, 初始化第二行就可以枚举任意与第一行合法的状态填充就可以了

合法判断: 这里合法判断比较复杂, 推荐把所有的合法状态存下来再做转移,不过我并没有这么做, 日后回顾的时候会注意的

转移方程:

d p [ r % 3 ] [ c u r ] [ l a s t ] = m a x ( d p [ r % 3 ] [ c u r ] [ l a s t ] , d p [ r − 1 % 3 ] [ l a s t ] [ l a s t ] + n u m [ c u r ] ) dp[r\%3][cur][last] = max(dp[r\%3][cur][last], dp[r-1\%3][last][last]+num[cur]) dp[r%3][cur][last]=max(dp[r%3][cur][last],dp[r1%3][last][last]+num[cur])

attention: 矩阵的 行和列

双倍经验: 两层状压 + 滚动数组优化, 很有价值的一题, 日后定要回顾一下

#include <bits/stdc++.h>
#include <bits/extc++.h>
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define all(a) a.begin(), a.end()
#define met(a, b) memset(a, b, sizeof(a))
#define what_is(x) cout << #x << " is " << x << endl
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define lowbit(x) x &(-x)
#define bin(x) cout << #x << " is " << bitset<10>(x) << endl
#define pi acos(-1.0)
using namespace std;
const int maxn = 1 << 10;
const int mod = 1e8;
int dp[3]<
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值