ACwing提高课-DP(状压DP)

一、状压DP

二、例题

1、小国王

  1. 基础模板题,不在赘述
  2. ACcode
#include 
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson p << 1, l, mid
#define rson p << 1 | 1, mid + 1, r
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS                      \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0)
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
ll powmod(ll a, ll b)
{
    ll res = 1;
    a %= mod;
    assert(b >= 0);
    for (; b; b >>= 1)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
template <class T>
inline void read(T &res)
{
    char c;
    T flag = 1;
    while ((c = getchar()) < '0' || c > '9')
        if (c == '-')
            flag = -1;
    res = c - '0';
    while ((c = getchar()) >= '0' && c <= '9')
        res = res * 10 + c - '0';
    res *= flag;
}
const int N = (1 << 10) + 5;
ll dp[13][N][102]; //第i行,j状态,已经放置了k个国王的方案数量
int n, k;
bool check(int x)
{
    for (int i = 0; i < n - 1; i++)
    {
        int l = (x >> i) & 1;
        int r = (x >> (i + 1)) & 1;
        if (l == r && l == 1)
            return 1;
    }
    return 0;
}
int get_cnt(int x)
{
    int res = 0;
    while (x)
    {
        if (x & 1)
            res++;
        x >>= 1;
    }
    return res;
}
int main()
{
    IOS;
    cin >> n >> k;
    dp[0][0][0] = 1;
    for (int i = 1; i <= n; i++) //行数
    {
        for (int j = 0; j < (1 << n); j++) //i层的状态
        {
            if (check(j)) //保证无相邻1
                continue;
            for (int jj = 0; jj < (1 << n); jj++) //i-1层状态
            {
                if (check(jj)) //保证无相邻1
                    continue;
                int x = j << 1;
                int xx = j;
                int xxx = j >> 1;
                if ((x & jj) || (xx & jj) || (xxx & jj))
                    continue;             //不合法
                int cnt_jj = get_cnt(jj); //当前层放置了jj个
                int cnt_j = get_cnt(j);
                for (int z = 0; z <= k; z++)//枚举已经放置了多少个国王
                {
                    if (z < cnt_jj || z + cnt_j > k)
                        continue;
                    dp[i][j][z + cnt_j] += dp[i - 1][jj][z];
                }
            }
        }
    }
    ll res = 0;
    for(int j = 0 ; j < (1 << n); j++)
        res += dp[n][j][k];
    cout << res;
    return 0;
}

2、玉米田

  1. 在第一题的基础上,增加了不可选的状态。
  2. 这里利用了预处理的思想,直接将所有合法的状态预处理存到vector中,这样会降低时间复杂度。
  3. ACcode
#include 
using namespace std;
typedef long long ll;
const int N = 15, M = (1 << 12) + 5;
const ll mod = 1e8;
int mp[N][N];
int n, m;
struct node
{
    int sta, cnt;
};
vector<node> ve;
int check(int x)
{
    int cnt = 0;
    for (int i = 0; i < m - 1; i++)
    {
        int l = (x >> i) & 1;
        int r = (x >> (i + 1)) & 1;
        if (l == r && l)
            return -1;
    }
    while (x)
    {
        if (x & 1)
            ++cnt;
        x >>= 1;
    }
    return cnt;
}

vector<node> e[M];
ll dp[N][M]; //前i行j状态放了z个
int True[N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        int tmp = 0;
        for (int j = 1; j <= m; j++)
        {
            tmp <<= 1;
            int x;
            cin >> x;
            tmp += x;
        }
        True[i] = tmp; //状压
    }
    for (int i = 0; i < (1 << m); i++) //预处理出合法的单行序列
    {
        int tmp = check(i);
        if (tmp == -1)
            continue;
        ve.push_back({i, tmp});
    }

    for (auto i : ve) //两个的合法状态
    {
        for (auto j : ve)
        {
            if ((i.sta & j.sta))
                continue;
            e[i.sta].push_back(j);
        }
    }
    dp[0][0] = 1; //初始化

    for (int i = 1; i <= n; i++) //前i行
    {
        for (auto j : ve) //枚举i行的当前状态
        {
            if ((j.sta | True[i]) != True[i]) //贫瘠土地不能种植
                continue;
            for (auto z : e[j.sta]) //枚举i-1行状态
            {
                if ((z.sta | True[i - 1]) != True[i - 1]) // //贫瘠土地不能种植
                    continue;

                dp[i][j.sta] = (dp[i][j.sta] + dp[i - 1][z.sta]) % mod;
            }
        }
    }
    ll ans = 0;
    for (auto j : ve) //统计答案
    {
        if ((j.sta | True[n]) != True[n])
            continue;
        ans = (ans + dp[n][j.sta]) % mod;
    }
    cout << ans;
    return 0;
}

你可能感兴趣的:(#,ACM&基础dp,动态规划)