• SCOI 2005 互不侵犯


    洛谷 P1896 [SCOI2005]互不侵犯

    洛谷传送门

    题目描述

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

    注:数据有加强(2018/4/25)

    输入格式

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

    输出格式

    所得的方案数

    输入输出样例

    输入 #1复制

    输出 #1复制

    题解:

    原谅我一开始看到题还以为是爆搜。。。

    其实是一道状态压缩的题目。

    蒟蒻自己一个比较大的进步就是把自己状态设置对了...

    设置:(dp[i][j][k])为第(i)行状态为(j)、已经用了(k)个国王时的方案数。

    状态压缩大体有这么几步:设置状态( ightarrow)考虑转移方式( ightarrow)按转移方式考虑预处理和判断转移条件( ightarrow)开始转移( ightarrow)统计答案。

    那么我们设置好状态,开始考虑转移方式:我们发现,若是想从第(i-1)行开始转移,转移的条件一是当前和上一次的状态,但是,这些状态的改变必然还会改变国王的个数。也就是说,这数组的两维是有联系的,是自变量和因变量的关系。所以我们因此想到,既然是自变量和因变量的关系,我们莫不如由此构建一个映射,存下来每个状态和每个状态需要的国王人数。这样我们转移的时候就没啥问题了。

    如何预处理呢?我们想到,我们需要按行处理状态,每个状态有放国王和不放国王两种选择。因为是预处理,我们是肯定不能用递推和(DP)的(你想干啥)

    所以我们考虑搜索。

    一次搜索可以处理出所有合法的行的方式。

    这里插一嘴,因为我们已经把所有合法的行的方式都求出来了,所以我们没必要再把(dp)数组的第二维开那么大,构建好映射关系之后,直接用(cnt)代替这个二进制状态即可。(因为(1-cnt)的每个数都对应着一个数组(s[i])作为状态。)

    然后再转移的时候进行判断是否合法就可以。

    转移方程:

    [dp[i][j][l]+=dp[i-1][k][l-num[j]] ]

    这里的(k,j)分别表示一种状态。

    代码:

    #include<cstdio>
    #define int long long
    using namespace std;
    int n,K,cnt,ans;
    int s[100],num[100];
    int dp[10][100][110];
    //dp[i][j][k]表示前i-1行放完,第i行状态为j、有k个国王时的方案数
    //状态0/1:0:国王攻击不到;1:被国王占领
    void dfs(int pos,int st,int tot)
    {
        if(pos>=n)
        {
            s[++cnt]=st;
            num[cnt]=tot;
            return;
        }
        dfs(pos+1,st,tot);
        dfs(pos+2,st+(1<<pos),tot+1);
    }
    signed main()
    {
        scanf("%lld%lld",&n,&K);
        dfs(0,0,0);
        for(int i=1;i<=cnt;i++)
            dp[1][i][num[i]]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<=cnt;j++)
                for(int k=1;k<=cnt;k++)
                {
                    if(s[j]&s[k])
                        continue;
                    else if(s[j]&(s[k]>>1))
                        continue;
                    else if(s[j]&(s[k]<<1))
                        continue;
                    else
                        for(int l=num[j];l<=K;l++)
                            dp[i][j][l]+=dp[i-1][k][l-num[j]];
                }
        ans=0;
        for(int i=0;i<=cnt;i++)
            ans+=dp[n][i][K];
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    webdriver 窗口切换
    element not visible 错误的原因和解决方式
    选择子数据,默认存储父数据 的校验方法
    What's jenkins And How to Install
    testng suite
    webdriver 选择下拉列表的操作
    webdriver 获取表格内的文案
    webdriver 定位表格元素
    webdriver 上传文件
    树上倍增求解LCA 模板
  • 原文地址:https://www.cnblogs.com/fusiwei/p/11814401.html
Copyright © 2020-2023  润新知