状态压缩经典题目(poj1184 nyoj81)

题目描述:

描述
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

分析:这个题目有个简化版本 poj3254 ,觉得这个有难度的可以先做一下那个题目。

首先我们求的是最多能放多少个炮兵,那么假如我把所有的情况都枚举了,然后在得到的结果里面找一个最大值,那么是不是就可以了。其实这个题目的思想就是这么简单。

但是我们如果用一般的枚举的方法肯定会超时,那么就用到了状态压缩。

因为这个题目中一个炮影响的是两行,所以我们要多定义一维的状态来表示当前行的上上一行的状态。

状态:dp【i】【j】【k】 第 i 行,状态为 k ,第 i-1 行状态为 j 的最大放的炮兵数目;

转移方程 dp [ i ] [ k ] [ t ]  = max ( dp [ i ] [ k ] [ t ] , dp [ i - 1 ] [ j ] [ k ] + num [ t ] );

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
int N,M;
char num[110],top;
int stk[70],cur[110];
int dp[110][70][70];

bool judge1(int x)  //判断二进制有没有相邻的1 或者相隔2位
{
    if((x&(x<<1)) || (x&(x<<2)))
        return false;
    return true;
}
bool judge2(int i,int x)
{
    return (cur[i]&stk[x]);
}
void jinit()
{
    top=0;
    int i,total=1<<N;
    for(i=0; i<total; i++) if(judge1(i)) stk[++top]=i;
}
int jcount(int x)
{
    int cnt=0;
    while(x)
    {
        if(x&1)
            cnt++;
        x>>=1;
    }
    return cnt;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&M,&N);
        jinit();
        char x;
        int ans=0;
        for(int i = 1; i <= M; i++)
        {
            getchar();
            cur[i]=0;
            for(int j=1; j<=N; j++)
            {
                scanf("%c",&x);
                if(x==‘H‘)
                    cur[i]+=(1<<(j-1));
            }
        }
        memset(dp,-1,sizeof(dp));
        for(int i = 1; i <= top; i++)
        {
            num[i]=jcount(stk[i]);
            if(!judge2(1,i))
            {
                dp[1][1][i]=num[i];
                ans=Max(ans,num[i]);
            }
        }
        int i,t,j,k;
        for(i = 2; i <= M; i++)
        {
            for(t = 1; t <= top; t++)  ///当前行
            {
                if(judge2(i,t)) continue;
                for(j = 1; j <= top; j++)  ///上上行
                {
                    if(stk[t]&stk[j])continue;
                    for(k = 1; k <= top; k++)  ///上一行
                    {
                        if(stk[t]&stk[k])continue;
                        if(dp[i-1][j][k]==-1)continue;
                        dp[i][k][t] =max(dp[i][k][t],dp[i-1][j][k]+num[t]);
                        ans = Max(ans,dp[i][k][t]);
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-08 14:13:31

状态压缩经典题目(poj1184 nyoj81)的相关文章

poj1185 状态压缩经典题

状态压缩的好题,直接求会爆内存,先把所有可能的状态求出来存在stk里,然后f[i][k][t]表示i行状态为t,i-1状态为k,由i-1状态来推出i状态即可 注意要打好边际条件的状态,并且某个可行状态必须由前一个可行状态推出 /* f[i][k][t]表示第i行状态为t,第i-1行状态为k的炮兵数 边际条件:第一行为任意可行状态即dp[1][1][i]=num[i] */ #include<bits/stdc++.h> using namespace std; #define ll long

P1052 过河(状态压缩)

P1052 过河(状态压缩) 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,--,L(其中L是桥的长度).坐标为0的点表示桥的起点,坐标为L的点表示桥的终点.青蛙从桥的起点开始,不停的向终点方向跳跃.一次跳跃的距离是S到T之间的任意正整数(包括S,T).当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥. 题目给出

NOJ 1118 玻璃球 【深蓝】[状态压缩DP]

玻璃球 [深蓝] 时间限制(普通/Java) : 10000 MS/ 30000 MS 运行内存限制 : 65536 KByte总提交 : 57            测试通过 : 11 比赛描述 玩过玻璃球游戏吗?sed同学小学没有毕业之前玩过,他常常用一个直径为d的圆柱管来装玻璃球,已知每个玻璃球是半径为 r1, r2, . . . , rn的球体,他当时一直有一个疑问:装这些玻璃球最少需要多长的圆柱管.现请你帮他解决这个问题. 假设玻璃球半径充分大,以至于管中不存在三个可以同时相互接触的玻

hdu4758AC自动机+状态压缩DP

http://acm.hdu.edu.cn/showproblem.php?pid=4758 Problem Description On the beaming day of 60th anniversary of NJUST, as a military college which was Second Artillery Academy of Harbin Military Engineering Institute before, queue phalanx is a special l

hdu1143 状态压缩dp 记忆化搜索写法

http://poj.org/problem?id=1143 Description Christine and Matt are playing an exciting game they just invented: the Number Game. The rules of this game are as follows. The players take turns choosing integers greater than 1. First, Christine chooses a

P1193扫雷 (DP状态压缩)

P1193扫雷 Accepted 标签:[显示标签] 描述 相信大家都玩过扫雷的游戏.那是在一个n*n的矩阵里面有一些雷,要你根据一些信息找出雷来.万圣节到了,"余"任过流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字表示和他8连通的格子里面雷的数目.现在棋盘是n*2的,第一列里某些格子是雷,而第二列没有雷,如: o 1 * 2 * 3 * 2 o 2 * 2 * 2 ('*'代表有雷,'o'代表无雷) 由于第一类的雷有可能有多种方案满足第二列的

codefroce 100589G (状态压缩)

题目:是说,给定n和k,有1,2,3,4,....n个数,求有多少这样的排列满足两个数之间的差的绝对值要小于等于k.(n<=15,k<=n) 解析:首先我们确定,n个数是必须要选的,所以,我们给定集合S(1.....1111)代表选了n个数.我们枚举结尾数字为i,用DP(S,i)代表选定集合S,以i为结尾数字满足条件的排列有多少..然后我们递归不断求集合上一位的个数.看注释吧. 1 #include <cstdio> 2 #include <cmath> 3 #incl

hdu 4057 AC自动机+状态压缩dp

http://acm.hdu.edu.cn/showproblem.php?pid=4057 Problem Description Dr. X is a biologist, who likes rabbits very much and can do everything for them. 2012 is coming, and Dr. X wants to take some rabbits to Noah's Ark, or there are no rabbits any more.

FZU1892接水管游戏-BFS加上简单的状态压缩和位运算处理

原题地址:http://acm.fzu.edu.cn/problem.php?pid=1892 Problem 1892 接水管游戏 Accept: 108    Submit: 498 Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description 接水管游戏的规则如下: 1.在N*N大小的方格上有两个特别的水管,分别为进水口和出水口: 2.有7种1*1大小的水管需要放在这N*N大小的方格内,使得水流能够从进水口经过这些