C++ --- сжатие состояния dp --- маленький король (один алгоритм в день 2023.4.15)

Меры предосторожности:
Предупредите о сложности дп!
Это название является приблизительным названием "State Compression dp—Мечта Мондриана" . Рекомендуется сначала прочитать эту статью и понять ее.

Название:
Расположите k королей на шахматной доске n × n, короли могут атаковать 8 соседних клеток, и найдите общее количество схем, которые не позволяют им атаковать друг друга.

Формат ввода
Всего одна строка, содержащая два целых числа n и k.

Формат вывода
Всего одна строка, указывающая общее количество решений, если ее нельзя разместить, выведите 0.

Диапазон данных
1≤n≤10,
0≤k≤n^2

输入:
3 2
输出:
16
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 12, M = 1 << 10, K = 110;
int n, m;                       //棋盘大小n*n, 有m个国王,
long long f[N][K][M];           //f[i][j][k],对于前i层的棋盘,总共放了j个国王,且第i层的状态为k(比如k是0101,1表示放了国王,0表示没放)的方案,
int c[M];                       //count[i]为第i层国王的数量,
vector<int> state;              //state存储所有合法状态,
vector<int> state_trans[M];     //state_trans[a] = {b1, b2, b3}存储能从合法状态a转移到的合法状态b,


bool check(int s) {
    
         //检查当前状态(二进制)是否存在连续的两个1,存在返回false不合法,不存在返回true合法。
    for (int i = 0; i<n; i++) {
    
    
        if ((s >> i & 1) && (s >> (i+1) & 1)) return false;
    }
    return true;
}
int count(int s) {
    
          //统计当前状态(二进制)中存在多少个1,也就是找到当前行中国王的个数。
    int res = 0;
    for (int i = 0; i<n; i++) res += (s >> i & 1);
    return res;
}

int main() {
    
    
    cin >> n >> m;

    //预处理所有合法方案
    for (int i = 0; i < (1<<n); i++) {
    
    
        if (check(i)) {
    
             //如果当前状态合法,存入state,并计算状态中国王的数量。
            state.push_back(i);
            c[i] = count(i);
        }
    }

    //预处理所有合法状态可以转移到的合法状态, 满足两种情况即可转移:
    //1.状态a和状态b的&,为0,代表不存在交集, (比如01001和01010,&后就是01000,还有1存在说明肯定有两个国王会相互攻击到)
    //2.状态a和状态b的|,不能存在两个连续的1,也就是需要转移后的方案是合法方案,
    for (auto &a : state){
    
    
        for (auto &b : state) {
    
    
            if ((a&b)==0 && check(a|b)) {
    
    
                state_trans[a].push_back(b);    //符合两个条件,就说明能够从a转移到的状态b
            }
        }
    }

    //dp
    f[0][0][0] = 1;   //初始化,对于前0行,总共摆放了0个国王,且第0行的状态为0(一个都没摆),是一种合法方案。
    for (int i = 1; i<=n+1; i++) {
    
                  //枚举棋盘的每一行(枚举到n+1行是一个小优化)
        for (int j = 0; j<=m; j++) {
    
                //枚举国王数量
            for (auto &a : state) {
    
                 //枚举合法状态a
                for (auto &b : state_trans[a]) {
    
       //枚举所有能够从a转移到的状态b
                    if (j - c[a] >= 0) {
    
            //如果当前剩余的国王足够摆放,就可以进行状态更新
                        f[i][j][a] += f[i-1][j-c[a]][b];
                    }
                }
            }
        }
    }
    //这里就将枚举到n+1行的优化体现出来了,f[n+1][m][0]代表,对于前n+1行,总共摆放了m个国王,且第i+1行的状态为0(一个都没摆),
    //那其实也就代表了前i行摆了m个国王的总方案数。
    cout << f[n+1][m][0];
    return 0;
}

Идея:
Грядет захватывающий дп (плачет), или классический метод дп в стиле y

1. Представление состояния
f[i][j][k] :
Для предыдущей строки шахматной доски (включая i-ю строку) iпоставлен jкороль , а iсостояние линии - kколичество шашек,
атрибут - Счет,

(Состояние k здесь относится к двоичному представлению «положения короля» в строке, например, 0101, что означает, что король находится на 2-й и 4-й сетках, что равно 5 в десятичной системе, что обычно используется в методе сжатия состояния dp для хранения состояния)

2. Расчет состояния
1. Прежде всего, давайте подумаем, какое состояние (одна строка) допустимо?
То есть для одного и того же ряда разрешено сохранять по крайней мере одно расстояние сетки между королем и королем.
(Картинка взята из заимствованного цветного ведущего босса , зеленый — король, а красный — дальность атаки):Пожалуйста, добавьте описание изображения

Во-вторых, давайте подумаем, при каких обстоятельствах возможно обновление статуса (две строки)?
То есть, когда оба состояния a и b являются допустимыми,
1. Операция AND a и b равна 0 (то есть a и b не могут иметь королей в одной и той же позиции)
2. Операция OR a и b Результат не существует последовательных королей (то есть результат является допустимым состоянием)
Пожалуйста, добавьте описание изображения

После выполнения вышеуказанных условий можно, наконец, выполнить передачу состояния:
f[i][j][a] += f[i-1][j-c[a]][b]

Непосредственно объясните из практического смысла:
- строка i, общее количество королей, размещенных на доске, равно j, а состояние строки i равно a, вы
можете начать с
- строки i-1, общее количество королей, размещенных на доске доска "Общее количество королей - количество королей в состоянии a", а состояние строки i - это b, и
выполняется перенос суперпозиции.

Если это полезно, пожалуйста, дайте бесплатный лайк ~ Кто-то, кто смотрит, - это мотивация, чтобы поддержать меня, чтобы записать!

Отказ от ответственности:
источником идеи алгоритма является г-н Y. Для получения подробной информации см. https://www.acwing.com/
Эта статья используется только для изучения записей и обменов

おすすめ

転載: blog.csdn.net/SRestia/article/details/130173445