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


    题目链接

    Solution [SCOI2005]互不侵犯

    题目大意:给定两个数(n),(k),求在(n^2)大小的棋盘内放置(k)个国王的方案数

    状压(dp)


    分析一下题目:(n)很小啊,(1 leq n leq 9),而且国王的攻击范围也很有特点,蒟蒻马上就想到了状压(dp)

    稍加分析即可列出(dp)的状态,以(f[i][s][k])来表示前(i)行,第(i)行的状态为(s),到第(i)行共计有(k)个国王的方案总数

    状态容易想,转移方程呢?

    (f[i][s][k] = sum f[i - 1][z][k - cnt(s)]),我们枚举由上一行哪个状态(z)转移而来,(cnt(x))表示二进制下数(x)(1)的总数(也就是状态(s)的国王个数)

    最后一点,边界:

    (f[1][s][cnt(s)] = 1),这个应该很好理解吧

    最后为了加速,我们可以先预处理出可能的状态(比如一行内两个国王紧挨着肯定是不行的)

    最后奉上蒟蒻的代码:

    Note:代码里的s是状态的编号

    #include <cstdio>
    using namespace std;
    const int maxn = 10;
    const int maxs = 1 << 10;//方案总数
    inline int cnt(int x){//统计二进制下1的数量
        int ret = 0;
        while(x){
            if(x & 1)ret++;
            x >>= 1;
        }
        return ret;
    }
    inline bool check(int x){//判断方案x是否可行(用于预处理)
        if(x & (x << 1))return false;
        if(x & (x >> 1))return false;
        return true;
    }
    int status[maxs],tot;//预处理出的方案,tot为方案总数
    long long f[maxn][maxs][maxn * maxn];//dp数组
    int N,K,full;
    int main(){
        scanf("%d %d",&N,&K);
        full = (1 << N) - 1;//full全集
        for(int i = 0;i <= full;i++)//预处理
            if(check(i))
                status[++tot] = i;
        for(int i = 1;i <= tot;i++)//边界条件
            f[1][i][cnt(status[i])] = 1;
        for(int i = 2;i <= N;i++)//dp
            for(int s = 1;s <= tot;s++)
                for(int k = cnt(status[s]);k <= K;k++)
                    for(int z = 1;z <= tot;z++){//枚举由上一行的状态z转移而来
                        int x = status[s];
                        int y = status[z];
                        if(x & y)continue;//判断当前行状态x与枚举的上一行状态y是否冲突
                        if(x & (y >> 1))continue;
                        if(x & (y << 1))continue;
                        f[i][s][k] += f[i - 1][z][k - cnt(status[s])];
                    }
        long long ans = 0;
        for(int i = 1;i <= tot;i++)//最后统计全都加起来
            ans += f[N][i][K];
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    linux sleep用法
    linux下set命令的参数及用法
    给vim编辑器自动添加行号
    linux一些基本常识(三)
    shell脚本面试题
    linux下字符串的比较方式
    浅谈Windows API编程
    WIN32 API ------ 最简单的Windows窗口封装类
    Microsoft函数调用约定
    Android UI 设计规范
  • 原文地址:https://www.cnblogs.com/colazcy/p/11514721.html
Copyright © 2020-2023  润新知