1595:炮兵阵地
时间限制: 1000 ms 内存限制: 524288 KB
提交数: 503 通过数: 322
【题目描述】
原题来自:NOI 2001
司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。一个 N×M 的地图由 N 行 M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
【输入】
第一行包含两个由空格分割开的正整数,分别表示 N 和 M;
接下来的 N行,每一行含有连续的 M 个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。
【输出】
仅一行,包含一个整数 K,表示最多能摆放的炮兵部队的数量。
【输入样例】
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
【输出样例】
6
【提示】
数据范围与提示:
N≤100,M≤10。
思路:
思路很简单吧。。(但是实现非常困难qwq,作为一个不怎么细心的孩子,调了三个小时qwq)
设置变量为f[i][j][k]表示已经处理到了第i行,第i行状态是b[j],第i-1行状态是b[k]
转移方程:f[i%3][k][j]=max(f[(i-1)%3][j][l]+sum[k],f[i%3][k][j]);
注意,这里的i%3应用的是滚动数组的思想。因为对于一个状态来说,需要记录的只是与之相关的三行,其它的都可以由转移得到,所以为了减小空间复杂度,防止MLE,我们只记录三行
做题时出问题的地方:
1.dp的时候把对应的sum[k]加成了sum[j]
2.f数组开小了,最后结果爆了
3.初始化出锅
代码(已AC):
#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[1050][1050];
int f[14][1050][1050];
int b[1020000];
int cnt;
int sum[102000];
int dat[102000];
int count(int x)
{
int num=0;
for(int i=0;i<m;i++)
{
if(x&(1<<i)) num++;
}
return num;
}
void prepare()
{
for(int i=0;i<(1<<m);i++)
{
if(!(i&(i<<1))&&!(i&(i<<2)))
{
cnt++;
b[cnt]=i;
sum[cnt]=count(i);
}
}
// for(int i=1;i<=cnt;i++) cout<<sum[i]<<" * ";
// cout<<'
';
}
void dp()
{
// for(int i=1;i<=n;i++) cout<<dat[i]<<" ";
// cout<<'
';
/*cout<<"f[1][i][1]:
";*/
for(int i=1;i<=cnt;i++)
{
if(!(b[i]&dat[1])) f[1][i][1]=sum[i]/*,cout<<f[1][i][1]<<" "*/;
}
/*cout<<'
';
int o=0;
cout<<"f[2][i][j]:
";*/
int q_sum=0;
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++)
{
// cout<<i<<" "<<j<<" "<<b[i]&b[j]<<" "<<b[i]&dat[2]<<" ">>b[j]&
if(!(b[i]&b[j])&&!(b[i]&dat[2])&&!(b[j]&dat[1]))
{
f[2][i][j]=sum[i]+sum[j];/*cout<<f[2][i][j]<<" "<<b[i]<<" "<<b[j]<<'
';*/
// o++;
q_sum=(q_sum+f[2][i][j])%100000;
}
}
/*cout<<"ooooooooooooooooo "<<o<<'
';*/
// cout<<dat[1]<<" "<<dat[2]<<" dat
";
}
/*cout<<'
';*/
int p=0;
/*for(int k=1; k<=n; k++) {
for(int i=0; i<(1<<m); i++) {
for(int j=0; j<(1<<m); j++)
/* cout<<f[k][i][j]<<" ";
cout<<'
';
}
cout<<'
';
}*/
/*for(int i=3; i<=n; i++)
for(int L=0; L<(1<<m); L++) { //i-1
if(L&b[i-1] || (L&(L<<1)) || (L&(L<<2))) continue; //特判
int q_sum=0; for(int S=0; S<(1<<m); S++) { //i
if(S&b[i] || L&S || (S&(S<<1)) || (S&(S<<2))) continue;
for(int FL=0; FL<(1<<m); FL++) { //i-2
if(FL&L || FL&S || FL&b[i-2] || (FL&(FL<<1)) || (FL&(FL<<2))) continue;
f[L][S][i%3]=max(f[L][S][i%3],f[FL][L][(i-1)%3]+sum[S]), //滚动数组的实现方法
p++;
}
}
}*/
for(int i=3;i<=n;i++)
for(int j=1;j<=cnt;j++)//第i-1行状态
{
if(!(b[j]&dat[i-1]))
for(int k=1;k<=cnt;k++)//i
{
if(!(b[j]&b[k]))
for(int l=1;l<=cnt;l++)//i-2
{
if(!(b[j]&b[k])&&!(b[k]&b[l])&&!(b[l]&b[j])&&!(b[k]&dat[i])&&!(b[j]&dat[i-1])&&!(b[l]&dat[i-2]))
{
f[i%3][k][j]=max(f[(i-1)%3][j][l]+sum[k],f[i%3][k][j]);
// q_sum+=f[(i-1)%3][j][l]+sum[k];
// q_sum%=1000000;
p++;
}
}
}
}
// cout<<p<<" pppppppppppppppp
";
// cout<<q_sum<<" summmmmmmmmmm
";
int ans=0;
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
{
ans=max(ans,f[n%3][i][j]);
}
cout<<ans<<'
';
/*for(int k=1; k<=n; k++) {
for(int i=0; i<(1<<m); i++) {
for(int j=0; j<(1<<m); j++)
cout<<f[k][i][j]<<" ";
cout<<'
';
}
cout<<'
';
}
*/
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>a[i][j];
for(int i=1;i<=n;i++)
{
int s=0;
for(int j=1;j<=m;j++)
{
if(a[i][j]=='P')
{
s=(s<<1)+0;
}
else
{
s=(s<<1)+1;
}
}
dat[i]=s;
}
/* for(int i=1;i<=n;i++) cout<<dat[i]<<" ";
cout<<'
';
cout<<"aghfukaku
";*/
prepare();
dp();
return 0;
}
ps:
做题经验:
1.平常做题的时候不要和题解对代码,不要跟着题解对拍,(特别是dp类题目,对拍根本拍不出来),最好是自己从头到尾看一遍自己的代码,检查一下是不是有什么细节问题
2.跟着自己的感觉走,不要老是看题解,因为题解的方法可能很多,不一定是你的方法
ヾ(◍°∇°◍)ノ゙