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;
}

posted @ 2020-10-30 11:32  Z_char  阅读(247)  评论(0编辑  收藏  举报