zoukankan      html  css  js  c++  java
  • bzoj1087 [SCOI2005]互不侵犯King

    1087: [SCOI2005]互不侵犯King

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 3306  Solved: 1915
    [Submit][Status][Discuss]

    Description

      在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
    左下右上右下八个方向上附近的各一个格子,共8个格子。

    Input

      只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

    Output

      方案数。

    Sample Input

    3 2

    Sample Output

    16
    分析:这道题求方案数,那么可以想到用dp或者搜索,甚至还可以用数学方法,然而对于本题而言,搜索需要记录的信息和状态太多,会TLE,也没有什么很好的数学方法来解决这道题,所以只有采用dp.
           注意到n非常小,而且状态需要记录的信息非常多,所以考虑状态压缩dp.对于这类棋盘问题,当前行的状态和上一行的状态有着密切联系,而且答案和k有关,所以我们设计状态dp[i][j][k]为前i行已经放了j个国王并且第i行的状态为k(二进制)的方案数,那么dp[i][j][k] = Σdp[i-1][j - num[k]][p],其中num数组记录着一行为状态k的放的国王的数目,p为上一行符合要求的状态.
           现在的问题就是如何判断状态i是否符合要求,这不仅与i本身有关,还和它上一行的状态j有关,利用 <<,>>,&就可以判断了,它的原理是什么呢?我们把每个状态抽象成1个二进制数,它的第i位表示第i列放不放国王,利用&的性质,可以判断上下两行是否冲突,那么如何判断当前行是否冲突呢?将i左移1位再与i进行&操作即可.
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int n, k,num[1000];
    long long dp[10][1000][1000];
    bool flag[1000];
    long long ans;
    
    void init()
    {
        for (int i = 0; i < (1 << n); i++)
            if (!(i & (i << 1)))
            {
                flag[i] = true;
                int t = i;
                while (t)
                {
                    num[i] += (t & 1);
                    t >>= 1;
                }
                dp[1][num[i]][i] = 1;
            }
    }
    
    int main()
    {
        scanf("%d%d", &n, &k);
        init();
        for (int i = 2; i <= n; i++)
            for (int j = 0; j <= k; j++)
                for (int now = 0; now < (1 << n); now++)
                {
                    if (!flag[now])
                        continue;
                    if (num[now] > j)
                        continue;
                    for (int last = 0; last < (1 << n); last++)
                    {
                        if (!flag[last])
                            continue;
                        if ((last & now) || ((now << 1) & last) || ((now >> 1) & last))
                            continue;
                        dp[i][j][now] += dp[i - 1][j - num[now]][last];
                    }
                }
        for (int i = 0; i < (1 << n); i++)
            ans += dp[n][k][i];
    
        printf("%lld", ans);
        return 0;
    }
  • 相关阅读:
    二叉查找树(二)
    白话红黑树系列之一——初识红黑树
    二叉查找树(三)
    白话红黑树系列之二——红黑树的构建
    二叉查找树(一)
    二叉查找树(五)
    选择实现—简单工厂
    嵌入式开发er的C语言能力自测(面试)题top 16
    AOP探索笔记
    浅谈.net插件式编程
  • 原文地址:https://www.cnblogs.com/zbtrs/p/6189240.html
Copyright © 2011-2022 走看看