• 【NOI2001】【Luogu P2704】【POJ1185】炮兵阵地


    状压DP。因为m<=10,所以把每一行部署炮兵部队的状态作为“状态”,二进制的每一位1表示部署,0表示没有。同时把山地表示为1,平原表示为0,第i行的地形为dx[i]。

    因为从上到下考虑,考虑第i行时要考虑第i-1,i-2行,而考虑i-1行时又要考虑第i-2,i-3行……所以令f[i][j][k]表示前i行,第i行状态为j,第i-1行状态为k时可以部署炮兵部队的最大值。

    可得以下方程:

    $f[i][j][k]=max(f[i-1][k][l]+cnt[j])(j,k,lin S )$

    同时j&k==0,j&l==0,j&dx[i]==0。

    初始化:$f[0][0][0]=0$,其它为$-infty$。

    S为一个行状态的合法集合,即状态中“1”两两之间间隔超过2。若一个状态i满足

    !(i&(i<<1))&&!(i&(i<<2))

    那么i是合法状态。

    答案:$max(f[n][i][j]) (i,jin S)$
    因为已初始化,所以找答案时可不用判断i&j是否为0。
    事实上,m=10时|S|=60,为了节省空间,可以把j,k定义为集合中相应状态的编号(下标)。框架中只要改初始化:
    $f[0][1][1]=0$(为什么,见代码↓)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n,m,s[102],cnt[102],tot;//s是可用集合,cnt是1的数目 
    int f[102][72][72];//f的2、3维都是可用状态的编号
    //经试验m=10时|s|=60,所以可以放心少开 
    int dixing[102];//0平原,1山地,状态&地形!=0即炮兵在山地上 
    char str[13];
    void input()//输入O(nm) 
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            scanf("%s",str);
            for(int j=0;j<m;++j)
            {
                dixing[i]<<=1;
                if (str[j]=='H')
                    dixing[i]|=1;
            }
        }
    }
    int count1(int x)//计算1的个数 
    {
        int sum=0;
        while(x) x-=x&-x,++sum;
        return sum;
    }
    void prework()//预处理合法集合O(2^m) 
    {
        for(int i=0;i<1<<m;++i)
            if (!(i&(i<<1))&&!(i&(i<<2)))
            {
                s[++tot]=i;
                cnt[tot]=count1(i);//计算1的个数 
            }
    }
    int main()
    {
        input();
        prework();
        memset(f,0xcf,sizeof(f));//f数组初始化 
        f[0][1][1]=0;
        for(int i=1;i<=n;++i)//DP O(n|s|^3) 
        for(int j=1;j<=tot;++j)
        for(int k=1;k<=tot;++k)
        for(int l=1;l<=tot;++l)
        if ((s[j]&s[k])==0&&(s[j]&s[l])==0&&(s[j]&dixing[i])==0)
            f[i][j][k]=max(f[i-1][k][l]+cnt[j],f[i][j][k]);
        int ans=0;//寻找答案 
        for(int i=1;i<=tot;++i)
            for(int j=1;j<=tot;++j)
                ans=max(f[n][i][j],ans);
        printf("%d",ans);
        return 0;
    }
    View Code

     

  • 相关阅读:
    ubuntu基本命令篇13用户管理
    网易邮箱繁体字信件乱码解决
    ubuntu基本命令篇16网络管理
    Ubuntu 10.04的安装
    DotNetNuke模块开发(一)
    查询进程打开的文件(转)
    Shell 的变量(转)
    Boot loader: Grub进阶(转)
    bash的通配符与特殊符号
    shell下的作业管理(转)
  • 原文地址:https://www.cnblogs.com/xzs123456/p/10988083.html
Copyright © 2020-2023  润新知