poj 1185 状压dp+优化

http://poj.org/problem?id=1185

炮兵阵地

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 29176   Accepted: 11303

Description

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

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

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

Input

第一行包含两个由空格分割开的正整数,分别表示N和M;

接下来的N行,每一行含有连续的M个字符(‘P‘或者‘H‘),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

Output

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

Source

Noi 01

很有趣的题目,由于炮兵攻击范围达到了两行所以我们枚举当前行状态时肯定需要前两行的状态,有一个显然的方程为 f[i][j][k]=MAX{  f[i-1][v][j] | if(i,j,k对应的三行的状态可以共存 }

每行状态最多210,这样算的话肯定会T,但是仔细想想会发现合法的状态最多只有60多个,因为只要存在相邻的1就不合法,所以可以筛去很多的状态,将这些状态离散化,a[i]为第i个合法状态,t[i]为第i个状态中‘1’的个数。接着我们就能用f[i][j][k]表示第i行为第k个合法状态,第i-1行为第j个合法状态时最大炮兵个数,复杂度大大降低。注意每次都要判断三个状态能否放在对应的三行上以及是否可以共存,这些关系的判断使用打表法存下,因为要进行多次判断,第一次直接计算结果T了,打表之后250ms A了,虽然不是很快- -

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 using namespace std;
 6 #define LL long long
 7 int e[105][15];
 8 int f[105][105][105];
 9 int N,M,tot,all;
10 int a[1005],t[1005];
11 int g[105][105];
12 int _g[105][105];
13 int sol(int x)
14 {
15     int r=0;
16     for(int j=0;j<M;++j)
17     {
18         if(x&(1<<j)) {
19             r++;
20             for(int i=-2;i<=2;++i)
21             {
22                 if(i==0||j+i<0||j+i>=M) continue;
23                 if(x&(1<<(j+i))) return -1;
24             }
25         }
26     }
27     return r;
28 }
29 void init()
30 {
31     for(int i=0;i<all;++i)
32     {
33         int x=sol(i);
34         if(x!=-1){
35             a[tot]=i;
36             t[tot++]=x;
37         }
38     }
39 }
40 bool ok(int _a,int x)
41 {
42     int A=a[_a];
43     for(int i=0;i<M;++i)
44         if((A&(1<<i))&&e[x][M-i]) return 0;
45     return 1;
46 }
47 bool match(int _a,int _b)
48 {
49     int A=a[_a],B=a[_b];
50     for(int i=0;i<M;++i)
51         if((A&(1<<i))&&(B&(1<<i))) return 0;
52     return 1;
53 }
54 int main()
55 {
56     int i,j,k=0;
57     while(scanf("%d%d",&N,&M)==2){tot=0;
58     all=(1<<M);memset(e,0,sizeof(e));
59     memset(f,0,sizeof(f));
60     memset(g,-1,sizeof(g));
61     memset(_g,-1,sizeof(_g));
62         for(i=3;i<=N+2;++i)
63            for(j=1;j<=M;++j){
64             char c;
65             scanf(" %c",&c);
66             e[i][j]=c==‘P‘?0:1;
67         }
68         init();
69         for(i=0;i<tot;++i)
70             for(j=3;j<=N+2;++j)
71             _g[i][j]=ok(i,j);
72         for(i=0;i<tot;++i)
73             for(j=0;j<tot;j++)
74         g[i][j]=match(i,j);
75         for(i=3;i<=N+2;++i)
76         {
77            for(int t1=0;t1<tot;++t1)
78            {
79                if(!_g[t1][i-2]) continue;
80                for(int t2=0;t2<tot;++t2)
81                {
82                    if(!_g[t2][i-1]||!g[t1][t2]) continue;
83                    for(int t3=0;t3<tot;++t3)
84                    {
85                        if(!_g[t3][i]||!g[t2][t3]||!g[t1][t3]) continue;
86                        if(f[i][t2][t3]<f[i-1][t1][t2]+t[t3])f[i][t2][t3]=f[i-1][t1][t2]+t[t3];
87                    }
88                }
89            }
90         }
91         int ans=0;
92         for(int t1=0;t1<tot;++t1)
93             for(int t2=0;t2<tot;++t2)
94             if(ans<f[N+2][t1][t2])ans=f[N+2][t1][t2];
95         printf("%d\n",ans);
96     }
97     return 0;
98 }
时间: 2024-11-06 17:47:05

poj 1185 状压dp+优化的相关文章

POJ 1185 状压DP

legal[] 保存所有在当前行可显示的状态,由dfs得到,len[]保存legal[]对应下标状态中的 1 的个数 , 也就是放置炮台的个数 state[i] 表示第 i 行这块区域的土地情况,H表示 1 ,P表示 0 那么每次加入一个legal状态  都要符合 !(legal[i] & state[k]) 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namesp

炮兵阵地(POJ 1185状压dp)

题意:n*m地图'H'能放'p'不能放,布兵的方格上下左右不能布兵,给你地图求最大布兵数 分析:关系到前两行,所以dp[i][j][k]第i行状态为j,i-1行状态为k时的最大布兵数, 先求出所有可行的状态,统计出其布兵数. #include <map> #include <set> #include <list> #include <cmath> #include <queue> #include <stack> #include

POJ 1038 状压DP

一个公司生产一种2*3规模的芯片,但是原材料上面有一些地方是不能用来当作芯片材料的,给出原料大小,及上面不能做原料的点,问你怎么分解,可以使生成芯片最大化. 对M进行三进制状压 last数组存储第i-1行和i-2行状态,cur数组存储i行和i-1行状态 cur[k]=2; // 本行k位置和上行k位置都不可用 cur[k]=1; // 本行k位置可用,上行k位置不可用 cur[k]=0; // 本行和上行位置k均可用 必须用滚动数组,否则爆内存 #include "stdio.h" #

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

/* 状压dp 刚开始&写成&&看了好长时间T0T. 状态转移方程 dp[i][k][j]=Max(dp[i][k][j],dp[i-1][l][k]+num[i][j]);(第i行的第j个状态有上一行的第k个状态得到) num[i][j]有两个功能,第一:判断第i行第j个状态是否合法 第二:判断第i行第j个状态的数目 */ #include<stdio.h> #include<string.h> #define N 110 int dp[N][N][N];

POJ 3254 (状压DP) Corn Fields

基础的状压DP,因为是将状态压缩到一个整数中,所以会涉及到很多比较巧妙的位运算. 我们可以先把输入中每行的01压缩成一个整数. 判断一个状态是否有相邻1: 如果 x & (x << 1) 非0,说明有相邻的两个1 判断一个状态能否放在该行: 如果 (a[i] & state) != state,说明是不能放置的.因为a[i]中存在某个0和对应state中的1,与运算之后改变了state的值 判断相邻两行的状态是否有同一列相邻的1: 如果(state & _state)不

POJ 2411 状压DP经典

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 16771   Accepted: 9683 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

Best Sequence(poj 1699) 状压dp(TSP)

类似于前两天做的那个wordstack.状压的其实有时候爆搜+记忆化也差不多. 就是这个是要与之前的都重合,移位预处理要注意. 理解好第一个样例就行 /* *********************************************** Author :bingone Created Time :2014/12/9 22:48:56 File Name :a.cpp ************************************************ */ #inclu

POJ 3254 状压DP

题目大意: 一个农民有一片n行m列 的农场   n和m 范围[1,12]  对于每一块土地 ,1代表可以种地,0代表不能种. 因为农夫要种草喂牛,牛吃草不能挨着,所以农夫种菜的每一块都不能有公共边. 告诉你 n ,m 和那些地方能种菜哪些地方不能种菜,求农夫一共有多少种方案种菜 解法: 基本思想是状压 也就是用一个int 型的数代表一行的种菜策略,二进制的0代表该位不能种菜,1位代表能种菜,使用位运算使处理速度变快 对于单行行,最多有2^12 种情况,并且 2^12种情况里面还有很多不满足题意的

POJ 2686(状压DP

第一次做状压感觉那一长串for显示了这是个多么暴力的算法呢...1A了倒是挺顺的 #include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<utility> #include<vector> #include<cstring> #include<cmath> #define INF 0x3fffffff #d