求卡常(AcWing328)三进制状压DP
  • 板块题目总版
  • 楼主Ex_咖喱棒
  • 当前回复5
  • 已保存回复5
  • 发布时间2022/10/13 14:18
  • 上次更新2023/10/27 07:41:50
查看原帖
求卡常(AcWing328)三进制状压DP
109060
Ex_咖喱棒楼主2022/10/13 14:18
// Author: kyEEcccccc

#pragma GCC optimize(2)
#pragma G++ optimize(2)

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long;

int n, m, k;
bool a[150][20];
int pw[20];
int f[60000], mx[60000];
int must[20], block[20];
vector<int> fre;

int at(int stt, int j)
{
    return stt / pw[j] % 3;
}
int at2(int stt, int j)
{
    return (stt >> j) & 1;
}

void init(void)
{
    for (int stt = 0; stt < pw[m]; ++stt)
        f[stt] = INT_MIN/2;
    int ini = 0;
    for (int j = 0; j < m; ++j)
        if (a[0][j] == 1) ini += pw[j]*2;
        else ini += pw[j];
    f[ini] = 0;
    
    for (int stt = 0; stt < pw[m]; ++stt)
        mx[stt] = f[stt];
    for (int j = 0; j < m; ++j)
    {
        for (int stt = 0; stt < pw[m]; ++stt)
            if (at(stt, j) == 1)
                mx[stt] = max(mx[stt], mx[stt-pw[j]]);
        for (int stt = 0; stt < pw[m]; ++stt)
            if (at(stt, j) == 2)
                mx[stt] = max(mx[stt], mx[stt-pw[j]]);
    }
}

bool checkput(int i, int put)
{
    if (at(put, 0) != 0) return 0;
    if (at(put, 1) == 1) return 0;
    if (i == 1)
    {
        for (int j = 0; j < m; ++j)
            if (at(put, j) == 2) return 0;
    }
    
    for (int j = 0; j < m; ++j)
    {
        if (at(put, j) == 1 && (a[i][j] || a[i-1][j] || a[i][j-1]
            || a[i-1][j-1] || a[i][j-2] || a[i-1][j-2]))
            return 0;
        if (at(put, j) == 2 && (a[i][j] || a[i-1][j] || a[i-2][j]
            || a[i][j-1] || a[i-1][j-1] || a[i-2][j-1]))
            return 0;
    }
    
    for (int j = 2; j < m; ++j)
    {
        if (at(put, j) == 1
            && (at(put, j-1) != 0 || at(put, j-2) != 0))
            return 0;
        if (at(put, j) == 2 && at(put, j-1) != 0)
            return 0;
    }
    
    return 1;
}

int getans(void)
{
    int mx = INT_MIN/2;
    for (int stt = 0; stt < pw[m]; ++stt)
        mx = max(mx, f[stt]);
    return mx;
}

void solve(void)
{
    for (int i = 1; i < n; ++i)
    {
//        cerr << "i: " << i << '\n';
        
        for (int stt = 0; stt < pw[m]; ++stt)
            f[stt] = INT_MIN/2;
        
        for (int put = 0; put < pw[m]; ++put)
        {
            if (!checkput(i, put)) continue;
            
//            cerr << "put: " << put << '\n';
            
            for (int j = 0; j < m; ++j)
                must[j] = 2;
            for (int j = 0; j < m; ++j)
            {
                if (at(put, j) == 1)
                {
                    must[j] = min(must[j], 1);
                    must[j-1] = min(must[j-1], 1);
                    must[j-2] = min(must[j-2], 1);
                }
                if (at(put, j) == 2)
                {
                    must[j] = min(must[j], 0);
                    must[j-1] = min(must[j-1], 0);
                }
            }
            
            int tot = 0;
            for (int j = 0; j < m; ++j)
                if (at(put, j) != 0) ++tot;
            
            fre.clear();
            for (int j = 0; j < m; ++j)
                if (must[j] == 2) fre.push_back(j);
            
            for (int cha = 0; cha < (1 << fre.size()); ++cha)
            {
                for (int l = 0; l < fre.size(); ++l)
                    if (at2(cha, l) == 1) must[fre[l]] = 1;
                
                int from = 0;
                for (int j = 0; j < m; ++j)
                    from += must[j]*pw[j];
                int curres = mx[from] + tot;
                
                for (int j = 0; j < m; ++j)
                {
                    if (a[i][j]) block[j] = 2;
                    else if (a[i-1][j]) block[j] = 1;
                    else block[j] = 0;
                }
                for (int j = 0; j < m; ++j)
                    if (must[j] == 2) block[j] = max(block[j], 1);
                for (int j = 0; j < m; ++j)
                {
                    if (at(put, j) == 1)
                        block[j] = block[j-1] = block[j-2] = 2;
                    if (at(put, j) == 2)
                        block[j] = block[j-1] = 2;
                }
                int to = 0;
                for (int j = 0; j < m; ++j)
                    to += block[j]*pw[j];
                    
//                if (curres >= 0)
//                    cerr << from << ' ' << to << ' ' << curres << '\n';
                
                f[to] = max(f[to], curres);
                
                for (int l = 0; l < fre.size(); ++l)
                    must[fre[l]] = 2;
            }
        }
        
//        cerr << pw[m] << endl;
//        for (int stt = 0; stt < pw[m]; ++stt)
//            if (f[stt] >= 0)
//                cerr << stt << " - " << f[stt] << '\n';
        
        for (int stt = 0; stt < pw[m]; ++stt)
            mx[stt] = f[stt];
        for (int j = 0; j < m; ++j)
        {
            for (int stt = 0; stt < pw[m]; ++stt)
                if (at(stt, j) == 1)
                    mx[stt] = max(mx[stt], mx[stt-pw[j]]);
            for (int stt = 0; stt < pw[m]; ++stt)
                if (at(stt, j) == 2)
                    mx[stt] = max(mx[stt], mx[stt-pw[j]]);
        }
//        cerr << getans() << '\n';
    }
}

void work(void)
{
    cin >> n >> m >> k;
    memset(a, 0, sizeof (a));
    for (int i = 1; i <= k; ++i)
    {
        int x, y;
        cin >> x >> y;
        a[x-1][y-1] = 1;
    }
    
    init();
    solve();
    cout << getans() << '\n';
}

int main(void)
{
    // freopen("i.txt", "r", stdin);
    ios::sync_with_stdio(0);
    cin.tie(NULL);

    pw[0] = 1;
    for (int i = 1; i <= 10; ++i)
        pw[i] = pw[i-1] * 3;

    int tsk;
    cin >> tsk;
    while (tsk--) work();

    return 0;
}

复杂度有点玄学,不过本来就玄学题,150*10的图开O2跑0.97s,AcWing上G飞了。求大佬帮卡。

2022/10/13 14:18
加载中...