炮兵阵地

题目链接

题目大意:在n*m的地图上放置炮兵,每个炮兵的攻击范围是上下左右两格内,有两种不同的地形,山地(用“H” 表示),平原(用“P”表示),只有平原可以布置炮兵,在不冲突的前提下最多可以布置多少炮兵?

这道题非常经典,我们用dp[i] [j] [k]表示第i行在第j种选取状态下,第i-1行在第k种选取状态下前i行最多摆放的炮兵数量。然后我们首先预处理每一行所有的合法状态,以降低时间复杂度。用num[i]表示第i行的合法状态数量,state[i] [j]表示第i行的第j种合法状态是什么,用temp[i]存储第i行的初始状态,用c[i]存储每种合法状态对应的炮兵数量。

可以不处理直接枚举所有状态吗

如果不预先处理合法状态,那么每一行所有的状态可能有2^10 = 1024种,由于炮兵的摆放需要考虑前两行的状态,那么三个循环枚举状态就会达到惊人的时间复杂度,所以预处理很关键!进行预处理我们会发现每一行的合法状态最多60种,这样即使是3个循环复杂度也会很低。

其他细节都在代码里了。

#include <bits/stdc++.h>
using namespace std;
int n,m,ans;
int dp[105][65][65];
int num[105],temp[105];
int state[105][65];
int c[1 << 10 + 5];
int count(int x){
int sum = 0;
while(x){
if(x & 1) sum++;
x >>= 1;
}
return sum;
}
int main(){
cin >> n >> m;
//每行的合法状态最多只有60种!
// int p = 0;
// for(int i = 0;i< (1 << 10);i++){
// int now = i;
// if((now & (now >> 1)) == 0 && (now & (now >> 2)) == 0) p++;
// }
// cout << p << endl;
for(int i = 1;i <= n;i++){
string s;
cin >> s;
for(int j = 0;j < m;j++){
if(s[j] == 'P') temp[i] += (1 << j);
}
}
//对第0行特殊处理
state[0][++num[0]] = 0;
//预处理合法状态
for(int i = 0;i < (1 << m);i++){
for(int j = 1;j <= n;j++){
int now = i;
if(!(now & (now >> 1)) && !(now & (now >> 2))
&& (now | temp[j]) == temp[j]){
state[j][++num[j]] = i;
c[i] = count(i);
}
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= num[i];j++){
int now = state[i][j];
//对第一行特殊处理,防止越界
if(i == 1) {
dp[i][j][1] = max(dp[i][j][1],c[now]);
continue;
}
for(int k = 1;k <= num[i - 1];k++){
int pre = state[i - 1][k];
if(!(now & pre)){
for(int l = 1;l <= num[i - 2];l++){
int pree = state[i - 2][l];
if(!(now & pree) && !(pre & pree)){
dp[i][j][k] = max(dp[i][j][k],dp[i - 1][k][l] + c[now]);
}
}
}
}
}
}
for(int i = 1;i <= num[n];i++){
for(int j = 1;j <= num[n - 1];j++){
ans = max(ans,dp[n][i][j]);
}
}
cout << ans << endl;
return 0;
}

排兵布阵 HDU4539

题目链接

大意:一个n*m的平原布置士兵。每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因也不是每一个位置都可以安排士兵。 (输入中1可以布置,0不可以布置)问:最多能安排多少个士兵。

思路和上一题基本一样,换汤不换药.

#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
int state[105],num[170],c[1 << 10 + 5],legal[105][170],dp[105][170][170];
int count1(int x){
int sum = 0;
while(x){
if(x & 1) sum++;
x >>= 1;
}
return sum;
}
bool ok(int now,int row){
return (now & (now >> 2)) == 0 && (now | state[row]) == state[row];
}
bool check(int now,int pre){
return (now & (pre >> 1)) == 0 && (now & (pre << 1)) == 0;
}
bool recheck(int now,int pree){
return (now & pree) == 0;
}
//由于是多组数据,所以每次都要初始化!
void init(){
ans = 0;
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
memset(state,0,sizeof(state));
}
int main()
{
memset(c,-1,sizeof(c));
while(~scanf("%d %d",&n,&m)){
init();
for(int i = 1;i <= n;i++){
for(int j = 0;j < m;j++){
int x;
scanf("%d",&x);
if(x) state[i] |= (1 << j);
}
}
//预处理合法状态
legal[0][++num[0]] = 0;
for(int i = 0;i < (1 << m);i++){
for(int j = 1;j <= n;j++){
if(ok(i,j)) {
legal[j][++num[j]] = i;
if(c[i] == -1) c[i] = count1(i);
}
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= num[i];j++){
int s1 = legal[i][j];
if(i == 1) {
dp[i][j][1] = max(dp[i][j][1],c[s1]);
continue;
}
for(int k = 1;k <= num[i - 1];k++){
int s2 = legal[i - 1][k];
if(check(s1,s2)){
for(int l = 1;l <= num[i - 2];l++){
int s3 = legal[i - 2][l];
if(recheck(s1,s3)){
dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][l] + c[s1]);
}
}
}
}
}
}
for(int i = 1;i <= num[n];i++){
for(int j = 1;j <= num[n - 1];j++){
ans = max(ans,dp[n][i][j]);
}
}
printf("%d\n",ans);
}
return 0;
}

二维状压DP经典题的更多相关文章

  1. HihoCoder - 1048 状压DP 经典题

    hihocoder题解说的十分清晰了,这份代码就是从讲解里学习的 方案数就是不断枚举合法状态下横放竖放或两者均可 合法判断的依据是记录当前行和下一行的状态 防止重复枚举的方法是先按行后按列 递归基瞎写 ...

  2. 二:状压dp

    一:状压dp的基本特征 状态压缩问题一般是指用十进制的数来表示二进制下的状态 这种用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩. 常用到位运算 二:位运算 &:与运 ...

  3. 【bzoj3195】【 [Jxoi2012]奇怪的道路】另类压缩的状压dp好题

    (上不了p站我要死了) 啊啊,其实想清楚了还是挺简单的. Description 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外.考古学家已经知道,这个文明在全盛时期 ...

  4. 【bzoj1087】【互不侵犯King】状压dp裸题(浅尝ACM-D)

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=54329606 向大(hei)佬(e)势力学(di ...

  5. [NOI2001] 炮兵阵地 (状压Dp经典例题)

    如果您的电脑比较优秀能在 1sec 内跑过 2^1000 的时间复杂度,不妨你可以尝试一下,其实实际时间复杂度远远少于 2^1000,作为骗分不错的选择QAQ,然后我们来分析一下正解: 很显然此题是一 ...

  6. 7月15日考试 题解(链表+状压DP+思维题)

    前言:蒟蒻太弱了,全打的暴力QAQ. --------------------- T1 小Z的求和 题目大意:求$\sum\limits_{i=1}^n \sum\limits_{j=i}^n kth ...

  7. POJ 3254 - Corn Fields - [状压DP水题]

    题目链接:http://poj.org/problem?id=3254 Time Limit: 2000MS Memory Limit: 65536K Description Farmer John ...

  8. hdu 1185 状压dp 好题 (当前状态与上两行有关系)

    /* 状压dp 刚开始&写成&&看了好长时间T0T. 状态转移方程 dp[i][k][j]=Max(dp[i][k][j],dp[i-1][l][k]+num[i][j]);( ...

  9. POJ 2411 状压DP经典

    Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 16771   Accepted: 968 ...

随机推荐

  1. 《视觉SLAM十四讲》学习日志(二)——初识SLAM

    小萝卜机器人的例子: 就像这种机器人,它的下面有一组轮子,脑袋上有相机(眼睛),为了让它能够探索一个房间,它需要知道: 1.我在哪——定位 2.周围环境怎么样——建图 定位和建图可以理解成感知的 &q ...

  2. CodeForces 494B Obsessive String ——(字符串DP+KMP)

    这题的题意就很晦涩.题意是:问有多少种方法,把字符串s划分成不重叠的子串(可以不使用完s的所有字符,但是这些子串必须不重叠),使得t串是所有这些新串的子串.譬如第一个样例,"ababa&qu ...

  3. elasticsearch _bulk api

    https://www.elastic.co/guide/cn/elasticsearch/guide/current/bulk.htmlbulk API 允许在单个步骤中进行多次 create . ...

  4. mysql用户与授权

    视图 create view 视图名   as  查询语句: MariaDB [hellodb]> create view view_left as select name from stude ...

  5. 区间dp括号匹配

    POJ2955 匹配则加一,不需要初始化 //#include<bits/stdc++.h> #include<iostream> #include<cstdio> ...

  6. Flutter移动电商实战 --(30)列表页_商品列表UI界面布局

    小程序里面的布局方式 小程序的图片上这里使用的是warp布局,因为首页里面火爆专区,已经用过了warp来布局了. 所以这里我们没有必要再讲一遍,这里我们使用ListView,我们把它布局成下图这种形式 ...

  7. pip 安装报错

    pip3 install uwsgi 报错 Command in /tmp/pip-build-5m77h_mm/uwsgi/ yum -y install python36-devel 解决

  8. ndroid如何监听开机广播和关机广播

    需求描述:有些时候,我们需要我们的程序在开机后能自动运行,在系统即将关闭时,能写入一些记录到指定的文件里. 一.开机广播监听: Android系统启动完成后会发出启动完成广播(android.inte ...

  9. Qt编写安防视频监控系统10-视频轮询

    一.前言 视频轮询在视频监控系统中是一个基础的核心功能,尤其是上了大屏以后,这个功能是必须的,根据预先设定的轮询间隔逐个加载视频到预先设定的通道画面数中,轮询间隔.轮询画面数.轮询采用的码流类型(主码 ...

  10. firewalld介绍

    (1).什么是firewalld? firewalld是提供了支持网络/防火墙区域(zone)定义网络链接以及接口安全等级的动态防火墙管理工具. (2).firewalld与iptables之间的关系 ...