《算法进阶指南》 DP状态压缩 炮兵阵地

炮兵攻击范围为两格,之枚举两行的状态不够。必须前两行转移给第三行。但这样210*2102^10会超时,去掉每行炮兵与高原冲突的状态,去掉每行可以互相攻击到的状态,剩余状态数<=60满足复杂度。
如果开1e3
(210)*(210)的空间会超内存,还得使用滚动数组。对应行数取模3即可。
值得注意的细节,将地形状态也压缩,令高原为1。这样只要 地形状态&炮兵状态 就能检查是否有冲突。假如当前炮兵状态为st,(st>>1)&st||(st>>2)&st,能够检查炮兵直接距离是否大于2。这样完全不需要按位遍历每一个状态,实现也很简单。

using namespace std;
// #define int long long
#define INF 1e9
#define endl '\n'

int n, m;

int fl[102][12];
int f[102][2003];
vector<int> allst[102];
void getvalidst()
{
    for (int line = 1; line <= n; line++)
    {
        for (int i = 0; i < (1 << m); i++)
        {
            int cnt = 2;
            int ok = 1;
            for (int j = 0; j < m; j++)
            {
                if (((i >> j)) & 1)
                {
                    if (fl[line][j + 1] == 0)
                    {
                        ok = 0;
                        break;
                    }
                    if (cnt >= 2)
                    {
                        cnt = 0;
                    }
                    else
                    {
                        ok = 0;
                        break;
                    }
                }
                else
                {
                    cnt++;
                }
            }
            if (ok == 1)
            {
                allst[line].push_back(i);
            }
        }
    }
}

bool oktrans(int st1, int st2, int st3)
{
    int ok = 1;
    for (int i = 0; i < m; i++)
    {
        int zwei1 = (st1 >> i) & 1;
        int zwei2 = (st2 >> i) & 1;
        int zwei3 = (st3 >> i) & 1;
        if (zwei1 + zwei2 + zwei3 > 1)
        {
            ok = 0;
        }
    }
    return ok;
}

int onenum(int st)
{
    int cnt = 0;
    for (int i = 0; i < m; i++)
    {
        cnt += (st >> i) & 1;
    }
    return cnt;
}
void solve()
{
    cin >> n >> m;

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            char tmp;
            cin >> tmp;
            if (tmp == 'P')
            {
                fl[i][j] = 1;
            }
            else
            {
                fl[i][j] = 0;
            }
        }
    }
    getvalidst();
    allst[0].push_back(0);
    for (int i = 0; i < allst[1].size(); i++)
    {
        f[1][allst[1][i]] = onenum(allst[1][i]);
    }
    for (int i = 2; i <= n; i++)
    {
        for (int ind1 = 0; ind1 < allst[i - 2].size(); ind1++)
        {
            for (int ind2 = 0; ind2 < allst[i - 1].size(); ind2++)
            {
                for (int ind3 = 0; ind3 < allst[i].size(); ind3++)
                {
                    int st1 = allst[i - 2][ind1], st2 = allst[i - 1][ind2], st3 = allst[i][ind3];
                    if (oktrans(st1, st2, st3))
                    {
                        f[i][st3] = max(f[i][st3], f[i - 2][st1] + onenum(st2) + onenum(st3));
                    }
                }
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < allst[n].size(); i++)
    {
        ans = max(ans, f[n][allst[n][i]]);
    }
    cout << ans << endl;
}

signed main()
{
#ifdef LOCAL_ENV
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(false), cin.tie(0);
    int T = 1;
    // cin >> T;
    for (int i = 1; i <= T; i++)
    {

        solve();
    }
    return 0;
}

收获:
1.状态压缩常见为可用n进制表示当前状态,前后两个阶段的状态需要“匹配”上才能转移。
2.状态压缩可以提前处理一个阶段的状态,将已经不合法的状态去除,减少阶段间转移的复杂度。
3.状态压缩一些位运算技巧。用&查重。

posted @ 2025-03-28 15:43  青一凡  阅读(18)  评论(0)    收藏  举报