• 状压DP之炮兵阵地


    题目

    原题来自:(NOI 2001)

    司令部的将军们打算在(N*M)
    的网格地图上部署他们的炮兵部队。一个(N*M)的地图由(N)(M)列组成,地图的每一格可能是山地(用 H表示),也可能是平原(用 P 表示)。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队),如果在地图中的灰色所标识的平原上部署一支炮兵部队,它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。其他位置攻击不到。炮兵的攻击范围不受地形的影响。现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

    输入格式

    第一行包含两个由空格分割开的正整数,分别表示(N)(M)
    接下来的(N)行,每一行含有连续的(M)个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。

    输出格式

    仅一行,包含一个整数(K),表示最多能摆放的炮兵部队的数量。

    样例

    样例输入

    5 4
    PHPP
    PPHH
    PPPP
    PHPP
    PHHP

    样例输出

    6

    思路

    这道题是一个很有趣的状压DP,由题意可知,对当前状态影响的有当前行状态,上一行状态,以及上上行的状态,对该行都有影响,所以,很显然我们枚举第(i)行,(i-1)行以及(i-2)的状态,对于任意一行(假设为(i)),那么我们对第(i)行进行合法化判断(仅对当前行),记录合法状态以及数目,方便下面枚举,还需要注意的是地图上的山地,需要单独开一个数组记录,与枚举状态求&,然后我们设(F)的三维数组,即(F[i][j][k])代表第i行第j种状态从上一行第k种状态转移过来的最优解,所以有(f[i][w][k]=max(f[i][w][k],f[i-1][k][j]+Q(w))),(f[i][w][k])上一状态为(f[i-1][k][j]),继承或求较大值更新

    如果还是不明白的话,代码奉上

    
    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1<<9;
    int f[100+10][maxn][maxn];//第i行第j种状态从上一行第k种状态转移过来的最优解
    int a[100+10];//记录地图山丘的数量
    int state[maxn];//记录合法的状态
    int tot,n,m;
    int lowbit(int x){//求最低位1
    	return x&-x;
    }
    int Q(int x){//判断第x种合法状态下的炮兵个数
    	x=state[x];
    	int cnt=0;
    	for(int i=x;i;i-=lowbit(i))cnt++;
    	return cnt;
    	
    }
    
    bool judge(int x){//判断状态x的合法性
    	if(x&(x<<1) || x&(x<<2))return 0;
    	return 1;
    }
    int main(){
    	
    	cin>>n>>m;
    	char ch;
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<m;j++){
    			scanf(" %c ",&ch);
    			if(ch=='H')a[i]+=(1<<j);//记录山丘位置
    		}
    	}
    	for(int i=0;i<(1<<m);i++){//记录合法状态
    		if(judge(i))state[++tot]=i;
    	}
    	for(int i=1;i<=tot;i++){//初始化第一行i种状态下的最优解,即为当前状态下的炮兵数量
    		if((a[1] & state[i])==0)f[1][i][1]=Q(i);
    	}
    	for(int i=2;i<=n;i++){//枚举第i行
    		for(int j=1;j<=tot;j++){//i-2行
    			if(state[j] & a[i-2])continue;//判断第i-2行
    			for(int k=1;k<=tot;k++){//枚举第i-1行
    				if(state[k] & a[i-1])continue;//判断第i-1行
    				if(state[k] & state[j])continue;//判断第i-1行与第i-2行
    				for(int w=1;w<=tot;w++){
    					if(state[w] & a[i])continue;//判断第i行
    					if(state[w] & state[k])continue;//判断第i行和第i-1行
    					if(state[w] & state[j])continue;//判断第i行和第i-2行
    					f[i][w][k]=max(f[i][w][k],f[i-1][k][j]+Q(w));//合法情况转移
    				}
    			}
    		}
    	}
    	int ans=0;
    	for(int i=1;i<=tot;i++){//求解
    		for(int j=1;j<=tot;j++){
    			if(a[n-1] & state[i])continue;
    			if(a[n] & state[j])continue;
    			if(state[i] & state[j])continue;
    			ans=max(f[n][j][i],ans);
    		}
    	}
    	cout<<ans<<endl;
    
    }
    
    
    
  • 相关阅读:
    MySQL技术内幕 InnoDB存储引擎 之 InnoDB体系架构
    ORACLE同义词使用
    五大好用的开源MySQL管理工具推荐
    MySQL Online DDL工具
    10046事件及其用法介绍
    MySQL表碎片清理
    MyRocks安装部署
    TiDB单机安装测试
    TiDB官方文档
    GoldenGate—AUTORESTART配置
  • 原文地址:https://www.cnblogs.com/soda-ma/p/13192678.html
Copyright © 2020-2023  润新知