POJ.1185 炮兵阵地

【题意】中文题目,题意很简单!

【解题方法】

首先我们求的是最多能放多少个炮兵,那么假如我把所有的情况都枚举了,然后在得到的结果里面找一个最大值,那么是不是就可以了。其实这个题目的思想就是这么简单。

但是我们如果用一般的枚举的方法肯定会超时,那么就用到了状态压缩。因为这个题目中一个炮影响的是两行,所以我们要多定义一维的状态来表示当前行的上上一行的状态。 状态:dp【i】【j】【k】 第 i 行,状态为 k ,第 i-1 行状态为 j 的最大放的炮兵数目

转移方程 dp [ i ] [ j ] [ k ]  = max ( dp [ i ] [ j ] [ k ] , dp [ i - 1 ] [k ] [ l ] + num [ j ] )

【AC代码和详细解释】

//
//Created by just_sort 2016/12/21
//Copyright (c) 2016 just_sort.All Rights Reserved
//

//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream> //isstringstream
#include <iostream>
#include <algorithm>
using namespace std;
//using namespace __gnu_pbds;
typedef long long LL;
//typedef pair<int, LL> pp;
#define REP(i, n) for(int i = 0; i < n; i++)
#define REPZ(i, n) for(int i = 1; i <= n; i++)
#define MP(x,y) make_pair(x,y)
const int maxn = 1100;
const int maxm = 1<<12;
const int inf = 0x3f3f3f3f;
//typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>order_set;
//head

int n, m, num[110], top; //记录每一行拥有的炮兵的个数
int st[70], cur[110];//每一行的转态和输入的地的状态
int dp[110][70][70]; //dp[i][j][k]在第i行状态为j,在第i-1行状态为k的最大方案数

bool judge1(int x)//判断二进制相邻的1或者间隔2个位置,即是炮兵横着不能攻击到
{
    if(x&(x<<1) || x&(x<<2)) return false;
    else return true;
}

bool judge2(int i, int k)//判断当前行和上一行的状态是否出现冲突
{
    return cur[i] & st[k];
}

void init1() //关键数组初始化
{
    memset(num, 0, sizeof(num));
    memset(st, 0, sizeof(st));
    memset(cur, 0, sizeof(cur));
    memset(dp, 0, sizeof(dp));
    top = 0;
}

void init2() //对不必要的状态进行剔除
{
    int total = 1 << m;
    for(int i = 0; i < total; i++)
    {
        if(judge1(i)) st[++top] = i;
    }
}

//int cal(int x) //计算状态x里面1的个数
//{
//    int cnt = 0;
//    while(x)
//    {
//        if(x&1) cnt++;
//        x >>= 1;
//    }
//    return cnt;
//}

int main()
{
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        init1();
        init2();
        char x;
        int ans = 0;
        for(int i = 1; i <= n; i++) //输入的地的状态
        {
            getchar();
            for(int j = 1; j <= m; j++)
            {
                scanf("%c", &x);
                if(x =='H') cur[i] += (1 << (j-1));
            }
        }
        //初始化DP装态
        for(int i=1; i<=top; i++)
        {
            num[i] = __builtin_popcount(st[i]);
            if(!judge2(1, i))
            {
                dp[1][i][1] = num[i];  //第1行状态为i,第0行状态为
                ans = max(ans, num[i]); //更新一下当前最大值
            }
        }
        //状态转移
        for(int i = 2; i <= n; i++) //枚举行
        {
            for(int j = 1; j <= top; j++)//当前行状态
            {
                if(judge2(i, j)) continue;
                for(int l = 1; l <= top; l++) //上上行状态
                {
                    if(st[j] & st[l]) continue;
                    for(int k = 1; k <= top; k++){ //上一行状态
                        if(st[j] & st[k]) continue;
                        dp[i][j][k] = max(dp[i][j][k], dp[i-1][k][l] + num[j]); //状态转移
                        ans = max(ans, dp[i][j][k]); //更新答案
                    }
                }
            }
        }
        //输出答案
        printf("%d\n", ans);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值