#状压dp#poj 1185 洛谷 2704 codevs 1647 loj 10173 vijos 1424 炮兵阵地

注:poj代码放在https://www.luogu.org/paste/oofcjs7e
有点坑,数据点 1 1 1 1 1 1 P P P搞不了(也就是说下面的代码非正解(正解还得搞一搞,上面的网址))
然后还有ssl 1384和jzoj 1768(没位置放标签了)


题目

在一个地图有平原也有山地,可以在平原放炮兵,攻击范围是一个边长为2( i i i(炮兵)到 i i i+2)的十字,炮兵不能相互攻击,问最多能摆放多少个炮兵


分析

用一个二进制数 x x x表示第 i i i行的炮兵状态,集合 S S S为二进制相邻1的距离不少于3的数(最多60个),设 c n t ( x ) cnt(x) cnt(x)为在二进制下1的个数, p d ( i , x ) pd(i,x) pd(i,x)为炮兵的位置属于平原,且 x x x属于 S S S集合a,预处理后开始动态规划,设 f [ n ] [ j ] [ k ] f[n][j][k] f[n][j][k]表示第 n n n行的状态为 j j j,第 n − 1 n-1 n1行状态为 k k k,前n行的最多摆放炮兵的数量
f [ n ] [ j ] [ k ] = m a x { f [ n − 1 ] [ k ] [ l ] + c n t ( j ) } f[n][j][k]=max\{f[n-1][k][l]+cnt(j)\} f[n][j][k]=max{f[n1][k][l]+cnt(j)}( j j j and k k k=0,$j $and l l l=0, k k k and l l l=0,pd(n,j),pd(n-1,k),pd(n-2,l)),否则为0


代码

#include <cstdio>
#include <vector>
#include <cstring>
const int make[10]={1,2,4,9,18,36,73,146,292,595};
struct rec{int x,y;}; std::vector<rec>vec;
int n,m,f[3][61][61],nap[101],ans;
int in(){
    int ans=0; char c=getchar();
    while (c<48||c>57) c=getchar();
    while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
    return ans;
}
int max(int a,int b){return (a>b)?a:b;}
int main(){
    n=in(); m=in(); int x=0;
    for (register int i=1;i<=n;i++){
        char c=getchar();
        while (c<65||c>90) c=getchar(); if (c=='H') nap[i]|=1<<m-1;
        for (register int j=1;j<m;j++) nap[i]|=getchar()=='H'?(1<<m-j-1):0;
    }
    for (register int i=0;i<=make[m-1];i++){
        bool flag=0;
        for (register int j=0;j<m-1&&!flag;j++) if ((i&(1<<j))&&(i&(1<<j+1))) flag=1;
        if (flag) continue;
        for (register int j=0;j<m-2&&!flag;j++) if ((i&(1<<j))&&(i&(1<<j+2))) flag=1;
        if (!flag) {
            rec c; c.x=i; c.y=0;
            for (register int j=0;j<m;j++) c.y+=i>>j&1;
            vec.push_back(c);
        }
    }
    for (register int i=0;i<vec.size();i++) 
    if (!(vec[i].x&nap[1])) f[1][i][vec.size()]=vec[i].y;
    for (register int i=0;i<vec.size();i++)
    if (!(vec[i].x&nap[2])){
        for (register int j=0;j<vec.size();j++)
        if (!(vec[j].x&nap[1])&&!(vec[i].x&vec[j].x))
        f[2][i][j]=max(f[2][i][j],f[1][j][vec.size()]+vec[i].y);
    }
    for (register int p=3;p<=n;p++){
        memset(f[x],0,sizeof(f[x]));
        for (register int i=0;i<vec.size();i++){
            if (vec[i].x&nap[p]) continue;
            for (register int j=0;j<vec.size();j++){
            	if (vec[j].x&nap[p-1]||vec[j].x&vec[i].x) continue;
                for (register int k=0;k<vec.size();k++){
                    if (vec[k].x&nap[p-2]||vec[j].x&vec[k].x||vec[i].x&vec[k].x) continue;
            	    if (p<n) f[x][i][j]=max(f[x][i][j],f[(x+2)%3][j][k]+vec[i].y);
            	    else ans=max(ans,f[(x+2)%3][j][k]+vec[i].y);
                }
            }
        }
        x=(x+1)%3;
    }
    return !printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值