Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 3160 Accepted: 950
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
经典的状态压缩DP,黑书原题,基本做法我就不多说了,反正是保存两行的状态去推第三行。
PKU上做过好多类似的题目,Mondriaan’s Dream就是其中最经典的。
然而本题直接用相似方法去搞,然后就直接TLE了。
于是开始了我漫长的代码优化过程。
首先是先用DFS把所有可行的状态转移存起来,结果MLE了。
后来发现有些状态还是无用的,于是先用DFS枚举所有可行状态,再DFS所有可行转移。
然后觉得还会TLE,于是又把可行状态转移按最后一行分类,这样可以直接在最后一行上减很多枝。
就这样写了,还是1.7s才过,实在想不到怎么样才能再优化了,或者是我的代码太次?
贴在这里了,大牛们帮我挑挑刺吧!
#include <stdio.h>
#include <memory.h>
#include <vector>
using namespace std;
int n,m;
int sts[102],ps[1<<10],pn;
char grids[102][12];
struct elem
{
int la,lb,tot;
elem(const int &aa=0,const int &bb=0,const int &tt=0):la(aa),lb(bb),tot(tt){}
};
int dps[2][1<<10][1<<10];
void input()
{
scanf("%d %d",&n,&m);
int i,j;
for (i=0;i<n;++i)
scanf("%s",grids[i]);
for (i=0;i<n;++i)
{
sts[i]=0;
for (j=0;j<m;++j)
if (grids[i][j]==’H’)
sts[i]|=(1<<j);
}
}
vector<elem> chs[1<<10];
void dfs(const int &curr,const int &la,const int &lb,const int &dep,const int &tot)
{
if (dep==m)
{
chs[curr].push_back(elem(la,lb,tot));
return;
}
dfs(curr,la,lb,dep+1,tot);
if (la&(1<<dep)) return;
if (lb&(1<<dep)) return;
if (dep>0&&(curr&(1<<(dep-1)))) return;
if (dep>1&&(curr&(1<<(dep-2)))) return;
dfs(curr|(1<<dep),la,lb,dep+1,tot+1);
}
void pdfs(const int &curr,const int &dep)
{
if (dep==m)
{
ps[pn++]=curr;
return;
}
pdfs(curr,dep+1);
if (dep>0&&(curr&(1<<(dep-1)))) return;
if (dep>1&&(curr&(1<<(dep-2)))) return;
pdfs(curr|(1<<dep),dep+1);
}
void init()
{
int t=(1<<m),i,j;
for (i=0;i<t;++i)
chs[i].clear();
pn=0;
pdfs(0,0);
for (i=0;i<pn;++i)
for (j=0;j<pn;++j)
dfs(0,ps[i],ps[j],0,0);
}
int dp()
{
memset(dps,-1,sizeof(dps));
dps[1][0][0]=0;
int t=(1<<m),i,j,k,curr,la,lb;
for (i=0;i<n;++i)
{
memset(dps[i&1],-1,sizeof(dps[i&1]));
for (j=0;j<pn;++j)
if (!(sts[i]&ps[j]))
{
for (k=0;k<chs[ps[j]].size();++k)
if (dps[1-(i&1)][chs[ps[j]][k].la][chs[ps[j]][k].lb]>-1&&dps[i&1][ps[j]][chs[ps[j]][k].la]<dps[1-(i&1)][chs[ps[j]][k].la][chs[ps[j]][k].lb]+chs[ps[j]][k].tot)
dps[i&1][ps[j]][chs[ps[j]][k].la]=dps[1-(i&1)][chs[ps[j]][k].la][chs[ps[j]][k].lb]+chs[ps[j]][k].tot;
}
}
int ans=0;
for (i=0;i<pn;++i)
for (j=0;j<pn;++j)
if (ans<dps[1-(n&1)][ps[i]][ps[j]])
ans=dps[1-(n&1)][ps[i]][ps[j]];
return ans;
}
int main()
{
input();
init();
printf("%d\n",dp());
return 0;
}
你是不是用4进制的?我实测后发现3进制直接算比4进制位运算要快一点,不过我两个都可以在PKU上AC :)