• 题解【洛谷P1896】[SCOI2005]互不侵犯


    题面

    棋盘类状压 DP 经典题。

    我们考虑设 (dp_{i,j,s}) 表示前 (i) 行已经摆了 (j) 个国王,且第 (i) 行国王摆放的状态为 (s) 的合法方案数。

    转移的时候枚举每一行的每种状态及已经摆放了的国王数量,然后枚举上一行的状态,如果合法就更新。

    但是这样做的时间复杂度大致是 (10 imes 10^2 imes 2^{10} imes 10^3 = 10^9) 的,明显会 TLE,于是我们考虑如何优化。

    其实我们在转移时有很多不合法的状态,即转移时的无用状态。于是我们考虑预处理一下所有在转移时合法的状态,这样优化后我们就可以 AC 本题了。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 13, maxm = 1 << 11, maxk = 103;
    
    int n, m, k;
    int cnt[maxm]; //存储每个状态中 1 的个数
    long long ans, dp[maxn][maxk][maxm];
    vector <int> g; //存储合法的状态
    vector <int> can[maxm]; //存储每个状态可以转移到的合法状态
    
    //判断一个状态是否合法,即判断是否有连续的 2 个 1
    inline bool check(int x) 
    {
        for (int i = 0; i < n; i+=1)
            if ((x >> i & 1) && (x >> (i + 1) & 1))
                return false;
        return true;
    }
    
    //求一个状态中 1 的个数
    inline int get_1(int x)
    {
        int sum = 0;
        for (int i = 0; i < n; i+=1)
            if (x >> i & 1)
                ++sum;
        return sum;
    }
    
    int main()
    {
        cin >> n >> m;
        for (int i = 0; i < (1 << n); i+=1) //枚举所有状态
            if (check(i)) //该状态合法
            {
                g.push_back(i); 
                cnt[i] = get_1(i);
            }
        int len = g.size(); //合法状态的个数
        for (int i = 0; i < len; i+=1)
            for (int j = 0; j < len; j+=1) //枚举每两个合法的状态
            {
                int a = g[i], b = g[j]; 
                if (!(a & b) && check(a | b))
                    can[i].push_back(j); //两个状态在转移时可以放相邻两行
            }
        dp[0][0][0] = 1;
        for (int i = 1; i <= n; i+=1) //枚举行数
            for (int j = 0; j <= m; j+=1) //枚举国王个数
                for (int k = 0; k < len; k+=1) //枚举状态
                {
                    int len1 = can[k].size(), geshu_1 = cnt[g[k]];
                    for (int ll = 0; ll < len1; ll+=1) //枚举上一行的状态
                    {
                        int l = g[can[k][ll]];
                        if (j >= geshu_1) //可以进行转移
                            dp[i][j][g[k]] += dp[i - 1][j - geshu_1][l];
                    }
                }
        for (int i = 0; i < (1 << n); i+=1) //枚举最后一行的状态
            ans += dp[n][m][i]; //统计答案
        cout << ans << endl; //输出答案
        return 0;
    }
    
  • 相关阅读:
    leetcode 268. Missing Number
    DBSCAN
    python二维数组初始化
    leetcode 661. Image Smoother
    leetcode 599. Minimum Index Sum of Two Lists
    Python中的sort() key含义
    leetcode 447. Number of Boomerangs
    leetcode 697. Degree of an Array
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(1月3日)
    北京Uber优步司机奖励政策(1月2日)
  • 原文地址:https://www.cnblogs.com/xsl19/p/12341501.html
Copyright © 2020-2023  润新知