P2704 [NOI2001] 炮兵阵地 状压DP

P2704 [NOI2001] 炮兵阵地
P2704 [NOI2001] 炮兵阵地 状压DP_第1张图片
H I N T HINT HINT
对于 100% 的数据, N ≤ 100 N≤100 N100 M ≤ 10 M≤10 M10,保证字符仅包含 p p p h h h

M的数据范围很小,考虑状压DP。

首先,影响第 i i i行的有 i − 1 i-1 i1行和 i − 2 i-2 i2行,所以我们的 d p dp dp数组要体现出来这两个数据的,同时体现出来自己现在在哪行。

所以我们可以开个三维的 d p dp dp数组: d p [ p r e ] [ n o w ] [ i ] dp[pre][now][i] dp[pre][now][i],其中的i表示当前正在处理第 i i i行,第 i i i行的状态集合为 n o w now now i − 1 i-1 i1行的状态集合为 p r e pre pre
为什么我们不开 d p [ p p r e ] [ p r e ] [ i ] dp[ppre][pre][i] dp[ppre][pre][i](我一开始是这样想的,后来在转移的时候发现不对) ,因为我们的第 i i i行的状态虽然受前两行影响,但当我们由 i − 1 i-1 i1转移来的时候就会经历 d p [ p p r e ] [ p r e ] [ i − 1 ] dp[ppre][pre][i-1] dp[ppre][pre][i1],也就体现了前两行满足了要求才能转移到现在的 n o w now now

其次我们看一下开的数组的大小。所有的集合的数量应该是 1 < < m 1<1<<m,那么我们要开的数组大小就是 1024 ∗ 1024 ∗ 100 1024*1024*100 10241024100会MLE,此时有两个解决方法:
Ⅰ滚动数组
Ⅱ由于不是所有的1024个都符合题中的摆放条件,所以我们只用处理出符合条件的那些,离散化一下就行了。

下文用的是第Ⅱ种方法

有的题解里面是处理出了所有①不考虑地图的符合条件的集合;
我在下面是处理出了②符合每一行地图的,并分别存起来。
如果是第①种,那么在后续的状态转移中还要多进行一次判断。

预处理的过程中,每个炮兵左右两个格子内不能有炮兵,我们让二进制中1代表有炮兵,0代表无,那么 ( s < < 2 ) (s<<2) (s<<2)就代表把整行向左移动了两位,如果它和本来的 s s s按位与后结果为不为0,那么就代表有俩棋子重了,就不行。其他的情况和这一样判断。

而判断一个行的集合是否和上两行的集合有某一列上冲突了,就直接把他们按位与再判断就行了。

#include
#define mem(a,b) memset(a,b,sizeof(a))
#define ll unsigned long long
#define  P pair <int,int>
//#define int ll
using namespace std;
ll mod = 1e9 + 7;
inline ll qpow(ll a, ll b) { ll ret = 1; while (b) { if (b & 1)ret = ret * a % mod; a = a * a % mod; b >>= 1; }return ret; }
inline int read() { int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }while ('0' <= ch && ch <= '9') { x = x * 10; x += ch - '0'; ch = getchar(); }return x * f; }
const int maxn = 110;
const int maxm = 12;
const int inf = 0x3f3f3f3f;
char mp[maxn][maxm];

struct node
{
    int data, num;
};
vector<node>G[maxn];
int n, m;
bool check(int s, int line)
{
    for (int i = 1;i <= m;i++)
    {
        if ((mp[line][i] == 'H') && (s & (1 << (i - 1))))
            return false;
    }
    return true;
}
int get_cnt(int x)
{
    int ret = 0;
    while (x)
    {
        if (x & 1)
            ret++;
        x /= 2;
    }
    return ret;
}
int dp[100][100][maxn];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1;i <= n;i++)
    {
        scanf("%s", mp[i] + 1);
    }
    for (int s = 0;s <= ((1 << m) - 1);s++)
    {
        if ((s << 1) & s) continue;
        if ((s << 2) & s) continue;
        if ((s >> 1) & s) continue;
        if ((s >> 2) & s) continue;
        int cnt = get_cnt(s);
        for (int i = 1;i <= n;i++)
        {
            if (check(s, i))
            {
                G[i].push_back({ s,cnt });
            }
        }
    }
    mem(dp, -inf);
    //dp[pre][now][i]
    for (int i = 0;i < G[1].size();i++)
    {
        dp[0][i][1] = G[1][i].num;
    }
    for (int i = 0;i < G[2].size();i++)
    {
        for (int j = 0;j < G[1].size();j++)
        {
            if (G[1][j].data & G[2][i].data)
                continue;
            dp[j][i][2] = max(dp[j][i][2], dp[0][j][1] + G[2][i].num);
        }
    }
    int ans=0;
    for (int i = 3;i <= n;i++)
    {
        for (int j = 0;j < G[i].size();j++)
        {
            for (int k = 0;k < G[i - 1].size();k++)
            {
                if (G[i][j].data & G[i - 1][k].data) continue;
                for (int l = 0;l < G[i - 2].size();l++)
                {
                    if (G[i - 1][k].data & G[i - 2][l].data) continue;
                    if (G[i][j].data & G[i - 2][l].data) continue;
                    dp[k][j][i] = max(dp[k][j][i], dp[l][k][i - 1] + G[i][j].num);
                    ans=max(ans,dp[k][j][i]);
                }
            }
        }
    }
    printf("%d\n", ans);

}

你可能感兴趣的:(DP)