《洛谷P2704 [NOI2001]炮兵阵地》

非常细细细的一题。

首先,这数据量显然是状压。

dp[i][j][k] 表示 到第i行,第i行状态为j,第i - 1行状态未k的最大方案数。

我们从上向下考虑的话,每个放置的棋子会被上面两行棋子的放置状态所影响。

所以我们每次转移的时候需要枚举上面的两行。

这样的话复杂度就是 n * m * m * m。

这显然已经超了(但是正解还就是这样)。

考虑后可以发现,很多状态都是会冲突被删掉的,这样复杂度就少了非常多,所以就够了。

所以我们可以先预处理出冲突的状态。

这里的判断很巧妙,首先把每一行的原图转换成二进制数,去判断山地有没有防炮台。

所以显然我们去把山地转成1,然后就可以位运算判断。

然后对于左右的判断也可以位运算来加速。

空间不够必须用滚动数组。

debug调到裂开。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 5005;
const int M = 1e5 + 5;
const LL Mod = 10007;
#define pi acos(-1)
#define INF 1e9 + 5
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int n,m,a[105];
int dp[3][1 << 10][1 << 10],num[1 << 10];
bool isvis[105][1 << 10];
int cal(int state)
{
    int num = 0;
    for(int j = 0;j < m;++j) num += ((state >> j) & 1);
    return num;
}
int main()
{
    n = read(),m = read();
    for(int i = 0;i < n;++i)
    {
        string s;cin >> s;
        for(int j = 0;j < s.size();++j) a[i] += (s[j] == 'H' ? (1 << j) : 0);
    }    
    for(int i = 0;i < (1 << m);++i) num[i] = cal(i);
    for(int i = 0;i < n;++i)
        for(int j = 0;j < (1 << m);++j)
        {
            if((j & a[i]) || (j & (j << 1)) || (j & (j << 2)) || (j & (j >> 1) || (j & (j >> 2)))) isvis[i][j] = 0;
            else isvis[i][j] = 1;
        }
    for(int i = 0;i < (1 << m);++i)
    {
        if(!isvis[0][i]) continue;
        int ma = num[i];
        dp[0][i][0] = ma;
        for(int j = 0;j < (1 << m);++j)
        {
            if(!isvis[1][j] || (i & j)) continue;
            int ta = ma + num[j];
            dp[1][j][i] = ta;
        }
    }
    for(int i = 2;i < n;++i)//line
    {
        for(int s1 = 0;s1 < (1 << m);++s1)
        {
            if(!isvis[i - 2][s1]) continue;
            for(int s2 = 0;s2 < (1 << m);++s2)
            {
                if(!isvis[i - 1][s2] || ((s1 & s2) == 1)) continue;
                for(int s3 = 0;s3 < (1 << m);++s3)
                {
                    if(!isvis[i][s3]) continue;
                    if((s1 & s3) || (s2 & s3)) continue;
                    dp[i % 3][s3][s2] = max(dp[i % 3][s3][s2],dp[(i - 1) % 3][s2][s1] + num[s3]);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0;i < (1 << m);++i)
        for(int j = 0;j < (1 << m);++j) ans = max(ans,dp[(n - 1) % 3][i][j]);
    printf("%d\n",ans);
    system("pause");
    return 0;
}
View Code

 

posted @ 2020-12-20 12:57  levill  阅读(99)  评论(0编辑  收藏  举报