Luogu P2704(炮兵阵地
这里就不赘述怎么进行\(DP\)了,预处理,状态设计,初始化,转移,把这些搞清楚就好了。
状态设计一定要不重不漏的将题目中所有情况划分为若干个集合。
然后考虑这若干个集合是怎么转移的,如果转移,代价是什么,都要考虑好。
设计状态之后,初始化一定要按照你所设计的状态来初始化,非法状态设成无穷,可能转移的设为0或者其他,不能想当然初始化。
预处理可能是你做着做着突然发现有某个地方复杂度很高,就是这里影响了你程序的复杂度,那就考虑是不是能快速搞一下,或者预处理出来。
对于这道题,主要讲一讲它的空间限制。
这题啊,这题其实是\(\mathcal{size~of~array~'f'~is~too~large}\)。
通俗的说,如果你还是要开\(f[N][(1<<N][(1<<N)))\),你会\(\mathcal{MLE}\)。
首先注意到\(N\)大而\(M\)较小,那肯定考虑状压一行,因为一行只有 列 个数,所以状态压缩比较小的,就不用开那么大了可以开\(f[N][(1<<M)][(1<<M)]\)。
然后考虑最多也就放\(N\times M\)个,不比之前求方案,显然可以用\(int\)稍微优化一点。
再然后注意到每次递推下一行,只需要前一行,那么可以滚动数组,用取模操作,然后就变成\(f[2][(1<<M)][(1<<M)]\),需要注意的是你不能直接把递推式里所有\(i\)和\(i-1\)加个\(%2\)就完事了,因为有减法的情况,万一出个负数,怎么取模?所以对存在减法的取模,要先加再模\((i-1+md)\%md\),因为这里\(md\)是\(2\),带进去就行。
什么?你觉得\(f[2][(1<<M)][(1<<M)]\)还不够优??好吧,那就再稍微优化一点,考虑到状态的定义,后两维是什么?表示的是第\(j\)种方案和第\(k\)种方案,那么我们可以把预处理部分单独拎出来,然后看一看对于某个\(M\),合法方案最多有多少,显然不到\(1<<M\),因为题目中还有炮兵攻击的限制,所以我们可以打表跑出来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int m;
ll cnt;
int main(){
scanf("%d",&m);
for(ll i=0;i<(1<<m);i++){
if(i&(i<<1))continue;
if(i&(i>>1))continue;
if(i&(i>>2))continue;
if(i&(i<<2))continue;
cnt++;
}
printf("%lld\n",cnt);
return 0;
}
我们发现,\(M\)达到10时,如果按照炮兵的要求,就算假设某一行全都能放,也就\(60\)种,所以开\(60\)的数组就行了,我的方案从\(1\)开始,就开的\(61\),可能\(60\)也能卡过去,但还是算了。
这里为了保险,建议即使算出来\(60\)也要多开一些,考试一定以得分为主。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m;
int situ[61],num[61],cnt;
int h[N];
int f[2][61][61];
int ans;
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<(1<<m);i++){
if(i&(i<<1))continue;
if(i&(i>>1))continue;
if(i&(i>>2))continue;
if(i&(i<<2))continue;
situ[++cnt]=i;
int j=i;
for(;j;j-=j&-j)num[cnt]++;
}
char t;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
cin>>t;
if(t=='H')h[i]^=(1<<j);//预处理每一行不能选择的最大状态
}
}
for(int i=1;i<=n;i++){
for(int t1=1;t1<=cnt;t1++){
if(i==1){
f[i][t1][1]=num[t1];
continue;
}
for(int t2=1;t2<=cnt;t2++){
for(int t3=1;t3<=cnt;t3++){
int op=situ[t1],op1=situ[t2],op2=situ[t3];
if((op&op1) || (op1&op2) || (op&op2))continue;
if(h[i-2]&op2)continue;//如果有交集就不行
if(h[i-1]&op1)continue;
if(h[i]&op)continue;
f[i%2][t1][t2]=max(f[i%2][t1][t2],f[(i+1)%2][t2][t3]+num[t1]);
if(i==n)ans=max(ans,f[i%2][t1][t2]);
}
}
}
}
printf("%d\n",ans);
return 0;
}