zoukankan      html  css  js  c++  java
  • 炮兵阵地 /// 状压DP oj26314

    题目大意:

    炮兵阵地 设置炮兵的位置 其上两位 下两位 左两位 右两位 不能同时设置炮兵

    这题是 corn fields玉米地 的升级版 可以先看下这题的注释 更详细些

    第一种方法是网上大多数题解的解法 https://blog.csdn.net/zwj1452267376/article/details/51387718

    第二种方法是在第一种的基础上

    预处理出每一行的可能状态,并直接保存其对应的下标 i , 这样可以通过下标 i ,

    直接找到对应的状态 may[i] 和该状态对应的1的个数 num[i] ,而不需要另外将行数与状态及个数对应存储

    将其建成邻接表的形式

    这样在枚举每一行的状态时 只要查询邻接表就行

    不需要枚举每种状态再去判断其是否能够对应该行的映像

    而找到该行邻接表中存放的状态下标 直接得到该状态及个数

    预处理出第一行的dp[][][],再依据第一行处理出第二行的dp[][][]

    这样在处理之后的各行时 就可以直接判断该行的前两行的状态了

    /// 第一种方法
    #include <bits/stdc++.h>
    using namespace std;
    int n,m,may[65],num[65];
    int mir[105],dp[105][65][65];
    int getnum(int w)
    { // 计算w中1的个数
        int cnt=0;
        while(w) {
            cnt+=(w & 1);
            w >>= 1;
        }
        return cnt;
    }
    int main()
    {
        while(~scanf("%d%d",&n,&m)) {
            int len=0;
            for(int i=0;i<(1<<m);i++) // 预处理m列所有可能状态及其1的个数
                if((i&(i<<1))==0 && (i&(i<<2))==0 && ((i<<1)&(i<<2))==0))
                    may[len]=i,num[len++]=getnum(i);
            //for(int i=0;i<len;i++) printf("%d %d
    ",may[i],num[i]);
            memset(mir,0,sizeof(mir));
            for(int i=0;i<n;i++) {
                char c[15]; scanf("%s",c);
                for(int j=0;j<m;j++)
                    if(c[j]=='H') mir[i]|=1<<j; // 处理出每一行的映像 利于寻找可能状态
            }
            memset(dp,0,sizeof(dp));
            for(int i=0;i<len;i++) // 预处理第一行
                if((mir[0]&may[i])==0)
                    dp[0][i][0]=max(dp[0][i][0],num[i]);
            for(int i=0;i<len;i++) // 预处理第二行
                if((mir[1]&may[i])==0)
                    for(int j=0;j<len;j++)
                        if((may[j]&mir[0])==0 && (may[j]&may[i])==0)
                            dp[1][i][j]=max(dp[1][i][j],dp[0][j][0]+num[i]);
            for(int i=2;i<n;i++) {  // 枚举更新的行数
                for(int j=0;j<len;j++) { // 枚举i行的可能状态
                    if((may[j]&mir[i])==0) {
                        for(int k=0;k<len;k++) { // 枚举i-1行的可能状态
                            if((may[k]&mir[i-1])==0 && (may[j]&may[k])==0) {
                                for(int p=0;p<len;p++) { // 枚举i-2行的可能状态
                                if((may[p]&mir[i-2])==0 && (may[p]&may[k])==0 && (may[j]&may[p])==0) 
                                    { dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+num[j]); }
                                }
                            }
                        }
                    }
                }
            }
            int ans=0;
            for(int i=0;i<len;i++)
                for(int j=0;j<len;j++)
                    ans=max(ans,dp[n-1][i][j]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code
    /// 第二种方法
    #include <bits/stdc++.h>
    using namespace std;
    
    int n,m,len; // 将图转为0 1图,1表示该位置可放炮兵
    int may[65], num[65]; // may[]保存m列的所有可能状态, num[]保存对应下标的may[]保存的状态包含几个1
    int dp[105][65][65]; // dp[i][j][k]=到i行为止1的总个数 i行状态为may[j],i-1行状态为may[k]
    vector <int> sta[105]; // 保存每行所有的可能状态对应的下标
    
    int get_num(int w)
    { // 得到w中有几个1
        int cnt=0;
        while(w) {
            cnt+=(w & 1);
            w >>= 1;
        }
        return cnt;
    }
    void init()
    { // m最大为10 先预处理出10列的所有可能状态
        for(int i=0;i<(1<<10);i++)
            if((i&(i<<1))==0 && (i&(i<<2))==0 
               && ((i<<1)&(i<<2))==0)
                may[len]=i, num[len++]=get_num(i);
    }
    void save_sta(int row,int mir)
    { // 保存row行的可能状态的对应下标,mir为row行的映像
        sta[row].clear();
        for(int i=0;i<len;i++) {
            if(may[i]>=(1<<m)) break; // 超过了m列的状态 直接忽略
            if(may[i]&mir) continue; // 不是row行的可能状态 则跳过
            sta[row].push_back(i); /// 保存符合的状态的对应下标
        }
    }
    
    int main()
    {
        init();
        while(~scanf("%d%d",&n,&m)) {
            for(int i=0;i<n;i++) {
                char c[15]; scanf("%s",c);
                int mir=0;
                for(int j=0;j<m;j++) // P为可放 H为不可放
                    if(c[j]=='H') mir|=1<<j; /// 处理出一个反过来的映像
                save_sta(i,mir); /// 利于与所有状态对照 
            }
    
            memset(dp,0,sizeof(dp));
            for(int i=0;i<sta[0].size();i++) {
                int t=sta[0][i], mt=may[t], nt=num[t]; 
                /// t即为可能状态的对应下标 则 may[t]为可能状态 num[t]为该状态中的1的数量
                dp[0][t][0]=max(dp[0][t][0],nt); // 更新第1行
                // 第一行只需考虑本身状态
    
                for(int j=0;j<sta[1].size();j++) {
                    int t1=sta[1][j], mt1=may[t1], nt1=num[t1];
                    if(mt1&mt) continue;
                    // 第二行只需考虑第一行的状态与其是否冲突
    
                    dp[1][t1][t]=max(dp[1][t1][t],dp[0][t][0]+nt1); // 更新第二行
                }
            }
    
            for(int i=2;i<n;i++) { // 更新第i行时, 需考虑i-1及i-2行是否冲突
                for(int j=0;j<sta[i].size();j++) { // 枚举第i行状态
                    int t1=sta[i][j], mt1=may[t1], nt=num[t1];
    
                    for(int k=0;k<sta[i-1].size();k++) { // 枚举i-1行状态
                        int t2=sta[i-1][k], mt2=may[t2];
                        if(mt1&mt2) continue; // i与i-1冲突则跳过
    
                        for(int p=0;p<sta[i-2].size();p++) { // 枚举i-2行的状态
                            int t3=sta[i-2][p], mt3=may[t3]; 
                            if((mt2&mt3) || (mt1&mt3)) continue; // i-2与i-1冲突 i-2与i冲突 则跳过
    
                            dp[i][t1][t2]=max(dp[i][t1][t2],dp[i-1][t2][t3]+nt);
                        } /// i行t1状态(i-1为t2)= max{ i-1行t2状态(i-2为t3) + i行1的个数 }
                    }
                }
            }
    
            int ans=-1;
            for(int i=0;i<sta[n-1].size();i++) {
                int t1=sta[n-1][i];
                for(int j=0;j<sta[n-2].size();j++) {
                    int t2=sta[n-2][j];
                    ans=max(ans,dp[n-1][t1][t2]);
                } /// 最后一行各种可能状态的最大值
            }
            printf("%d
    ",ans);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    【转载】分析商品日均销量(DMS)对促销商品选择的意义
    日志备份和差异备份还原中的常见问题示例(转自&邹建)
    SQL Server 2000中的完整备份、差异备份操作
    数据库差异备份与增量备份的不同之处
    差异备份和还原操作方法(转)
    SQL备份(全)
    Microsoft SQL2000 错误代码 (@@error)
    图解SQL的inner join(join)、left join、right join、full outer join、union、union all的区别
    arm-none-linux-gnueabi-gcc command not found
    关于ST-Link的internal command error问题的解决方法
  • 原文地址:https://www.cnblogs.com/zquzjx/p/9207713.html
Copyright © 2011-2022 走看看