1. 为何状态压缩:

棋盘规模为n*m,且m≤10,如果用一个int表示一行上棋子的状态,足以表示m≤10所要求的范围。故想到用int s[num]。至于开多大的数组,可以自己用DFS搜索试试看;也可以遍历0~2^m-1,对每个数值的二进制表示进行检查;也可以用数学方法(?)

2. 如何构造状态:

当然,在此之前首先要想到用DP(?)。之后,才考虑去构造状态函数f(...)。

这里有一个链式的限制 :某行上的某个棋子的攻击范围是2。即,第r行的状态s[i],决定第r-1行只能取部分状态s[p];同时,第r行的状态s[i],第r-1行状态s[p],共同决定第r-2行只能取更少的状态s[q]。当然,最后对上面得到的候选s[i], s[p], s[q],还要用地形的限制去筛选一下即可。

简言之,第r行的威震第r-2行,因此在递推公式(左边=右边)中,必然同时出现r,和r-2两个行标;由于递推公式中行标是连续出现的,故在递推公式中必然同时出现r, r-1和r-2三个行标。由于在递推公式中左边包含一个f(...),右边包含另一个f(...),根据抽屉原理,r, r-1, r-2中至少有两个在同一个f(...)中,因此状态函数中必然至少包括相邻两行的行号作为两个维度。这就是为什么状态函数要涉及到两(相邻的)行,而不是一行。能想到的最简单形式如下:

dp[r][i][p]:第r行状态为s[i],第r-1行状态为s[p],此时从第0行~第r行棋子的最大数目为dp[r][i][p]

递推公式:

s[p]影响到s[q]的选取

----

|    |

dp[r][i][p]=max{dp[r-1][p][q]}+sum[j], 其中sum[j]是状态s[j]中1的个数

|   |                             |

----                             |

s[i]影响到s[p]的选取                 |

|                                 |

----------------------------

代码如下:

 #include <stdio.h>
#include <string.h>
#include <iostream>
#define MAX(a,b) (a)>(b)?(a):(b)
using namespace std;
int dp[][][]; //d[i][j][k]: “第i行状态是s[j],第i-1行状态是s[k]”的
int s[]; //一行的状态选择s[0], s[1], ... , s[k-1]
int n,m; //n行×m列
int k; //一行的所有状态数
int map[]; //'H''P'地图map[0]~map[n-1],地图每一行map[line]: 1001 表示HPPH
int sum[]; /*
很久就看推荐题目有这个了,一直没做,因为看了好几次没看懂,都说dp,这几天看了状态压缩后明白了,其实就是用
二进制来表示各个位置的状态然后进行枚举,把状态放进数组里就行,在这里用dp[i][j][k]表示第i行,当前j状态,
i-1行是k状态时候的最大炮数 dp[i][j][k]=MAX(dp[i][j][k],dp[i-1][k][p]+sum[j]) CAUTION:
1. 所有下标均从0开始
2. m<=10保证了可以用一个int存储一行的状态
*/ //状态s[x]是否造成行冲突
bool ok(int x)
{
if(x&(x<<))return false;
if(x&(x<<))return false;
return true;
} //状态s[x]下有多少个1
int getsum(int x)
{
int num=;
while(x>)
{
if(x&)num++;
x>>=;
}
return num;
} void find()
{
memset(s,,sizeof(s));
for(int i=;i<(<<m);i++) //i枚举所有m位的二进制数
{
if(ok(i))
{
s[k]=i;
sum[k++]=getsum(i);
} }
} int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
memset(dp,-,sizeof(dp)); int i;
for(i=;i<n;i++){
for(int j=;j<m;j++){
char tmp;
cin>>tmp;
if(tmp=='H')map[i]=map[i]|(<<j);//把第i行原始状态取反后放入map[i]
}
} k=;
find(); //1. 初始化第0行状态(只考虑有效状态,无效状态为-1)
for(i=;i<k;i++)
if(!(s[i]&map[])) //s[i]为1的位如果对应平原(0),则&运算后为0
dp[][i][]=sum[i]; //2. 计算第1~n-1行状态(碰到无效状态,continue)
for(int r=;r<n;r++)
{
for(int i=;i<k;i++)//枚举第r行的状态 s[i]
{
if(map[r]&s[i]) continue; //通过地形排除部分第r行的状态 for(int p=;p<k;p++) //枚举第r-1行状态 s[p]
{
if(s[i] & s[p]) continue; //r与r-1没有想接触的 for(int q=;q<k;q++) //枚举第r-2行状态s[q]
{
if(s[p] & s[q]) continue; //Sam:这行是我加的
if(s[i] & s[q]) continue; //r与r-2行没有接触的 if(dp[r-][p][q]==-) continue; //所有不可能的情形dp[i][j][k]都为-1(初始化的值)
dp[r][i][p]=MAX(dp[r][i][p],dp[r-][p][q]+sum[i]);
}
}
}
} int ans=;
for(i=;i<k;i++)
for(int j=;j<k;j++)
ans=MAX(ans,dp[n-][i][j]);
printf("%d\n",ans);
} system("pause");
return ;
}

POJ 1185 状态压缩DP(转)的更多相关文章

  1. POJ 1185 状态压缩DP 炮兵阵地

    题目直达车:   POJ 1185 炮兵阵地 分析: 列( <=10 )的数据比较小, 一般会想到状压DP. Ⅰ.如果一行10全个‘P’,满足题意的状态不超过60种(可手动枚举). Ⅱ.用DFS ...

  2. poj 2923(状态压缩dp)

    题意:就是给了你一些货物的重量,然后给了两辆车一次的载重,让你求出最少的运输次数. 分析:首先要从一辆车入手,搜出所有的一次能够运的所有状态,然后把两辆车的状态进行合并,最后就是解决了,有两种方法: ...

  3. poj 2688 状态压缩dp解tsp

    题意: 裸的tsp. 分析: 用bfs求出随意两点之间的距离后能够暴搜也能够用next_permutation水,但效率肯定不如状压dp.dp[s][u]表示从0出发訪问过s集合中的点.眼下在点u走过 ...

  4. Mondriaan's Dream(POJ 2411状态压缩dp)

    题意:用1*2的方格填充m*n的方格不能重叠,问有多少种填充方法 分析:dp[i][j]表示i行状态为j时的方案数,对于j,0表示该列竖放(影响下一行的该列),1表示横放成功(影响下一列)或上一列竖放 ...

  5. poj 2411 状态压缩dp

    思路:将每一行看做一个二进制位,那么所有的合法状态为相邻为1的个数一定要为偶数个.这样就可以先把所有的合法状态找到.由于没一层的合法状态都是一样的,那么可以用一个数组保存.由第i-1行到第i行的状态转 ...

  6. poj 3254 状态压缩DP

    思路:把每行的数当做是一个二进制串,0不变,1变或不变,找出所有的合法二进制形式表示的整数,即相邻不同为1,那么第i-1行与第i行的状态转移方程为dp[i][j]+=dp[i-1][k]: 这个方程得 ...

  7. poj 1185 状态压缩

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 27926   Accepted: 10805 Descriptio ...

  8. POJ 3254 状态压缩 DP

    B - Corn Fields Crawling in process... Crawling failed Time Limit:2000MS     Memory Limit:65536KB    ...

  9. poj 1185(状态压缩DP)

    poj  1185(状态压缩DP) 题意:在一个N*M的矩阵中,‘H'表示不能放大炮,’P'表示可以放大炮,大炮能攻击到沿横向左右各两格,沿纵向上下各两格,现在要放尽可能多的大炮使得,大炮之间不能相互 ...

随机推荐

  1. FZOJ--2221-- RunningMan 福建第六届省赛

    题目链接:http://acm.hust.edu.cn/vjudge/contest/127149#problem/J 题目大意: 因为总共就分三个队,因为两个队都要选取最优的策略,不论B队咋放,要使 ...

  2. 日志配置logback

    在选择项目日志框架时,发现log4j的作者开发了新的日志框架,据说性能提高不少,那就选它了,不过,除了配置上有点不习惯外,最重要的一点 ,打印线程号这个功能依然没有(打印线程名这个东西是在是个鸡肋). ...

  3. 使用python爬取P站图片

    刚开学时有一段时间周末没事,于是经常在P站的特辑里收图,但是P站加载图片的速度比较感人,觉得自己身为计算机专业,怎么可以做一张张图慢慢下这么low的事,而且这样效率的确也太低了,于是就想写个程序来帮我 ...

  4. win7IIS错误修改路径最全的

    http://blog.csdn.net/testcs_dn/article/details/8726480 http://www.myexception.cn/asp-dotnet/1341569. ...

  5. poj 2828 Buy Tickets【线段树单点更新】【逆序输入】

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 16273   Accepted: 8098 Desc ...

  6. http协议和web本质

    转载:http://www.cnblogs.com/dinglang/archive/2012/02/11/2346430.html http协议和web本质 当你在浏览器地址栏敲入“http://w ...

  7. 【python自动化第五篇:python入门进阶】

    今天内容: 模块的定义 导入方法 import的本质 导入优化 模块分类 模块介绍 一.模块定义: 用来在逻辑上组织python代码(变量,函数,逻辑,类):本质就是为了实现一个功能(就是以.py结尾 ...

  8. Yii 安装二维码扩展Qrcode

    比如要添加 https://github.com/2amigos/yii2-qrcode-helper 生成二维码的 这个扩展第一种方法 :    1.打开根目录的composer.json, 在re ...

  9. 关于ABAP事件的一张图

    事件: 这里有几组事件关键字 ,这些事件关键字在特定环境下控制ABAP/4 程序流. 逻辑数据库 是典型报表程序的外部流控制的中心点.如果将逻辑数据库链接到报表 程序,将导致显示选择 屏幕,并决定系统 ...

  10. proxy 出现乱码问题解决 lua

    ngx.req.set_header("Accept-encoding", ""); 加上这句 解除乱码 压缩问题造成的乱码