zoukankan      html  css  js  c++  java
  • 【SCOI2005】互不侵犯 题解(状压DP)

    前言:一道状压DP的入门题(可惜我是个DP蒟蒻QAQ)

    ------------------

    题意简述:求在一个$n*n$的棋盘中放$k$个国王的方案数。注:当在一个格子中放入国王后,以此格为中心的九宫格的其他八个格子将不能放置国王。

    数据范围:$1leq nleq 9$,$1leq kleq n*n$。

    ------------------------------

    看到数据范围,不是$dfs$就是状压DP。这道题我们考虑状压DP。

    状压DP就是把某个阶段转换成二进制记录下来,一般用于数据范围较小的题目,状压因此得名。

    国王个数是一个限制条件,所以这是一个阶段。在状压DP中,我们一般考虑以行作为阶段。因为上一行的放置情况关系到这一行的放置情况,所以我们还要再用一维表示放置情况,这一维要用到状压。

    所以我们设$f[i][j][k]$为在前$i$行中放入$k$个国王,且这一行的摆放情况为$j$的方案数。摆放情况可以用$dfs$预先处理。

    考虑转移过程中的限制条件:

    如果$ sit[j]$ 与 $sit[k]=1 $,则表示上下相邻的格子都摆放了国王。

    如果$ (sit[j]<<1)$ 与 $sit[k]=1 $,则表示左上或右下摆放了国王。

    如果$ sit[j]$ 与 $(sit[k]<<1)=1 $,则表示右上或左下的格子摆放了国王。

    左右相邻的情况在$dfs$中即可排除。

    转移方程:$f[i][j][s]+=f[i-1][k][s-num[j]]$。$num[j]$表示放置情况为$j$时国王的放置个数。

    边界:$f[1][i][num[i]]=1$。

    其实貌似可以用滚动数组优化来省掉第一维,但我懒得写了。

    注意开$long long$。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,m,cnt=0,ans=0;
    int sit[2005],num[2005];//预处理放置情况
    int f[10][2005][100];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void dfs(int pos,int sum,int node)
    {
        if (node>=n)
        {
            sit[++cnt]=pos;
            num[cnt]=sum;
            return;
        }
        dfs(pos,sum,node+1);//在此格不放入国王
        dfs(pos+(1<<node),sum+1,node+2);//在此格放入国王,此时要跳过相邻的格子。
    }
    signed main()
    {
        n=read(),m=read();
        dfs(0,0,0);
        for (int i=1;i<=cnt;i++) f[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 (sit[j]&sit[k]) continue;
                    if ((sit[j]<<1)&sit[k]) continue;
                    if (sit[j]&(sit[k]<<1)) continue;
                    for (int s=m;s>=num[j];s--) f[i][j][s]+=f[i-1][k][s-num[j]];//转移
                }
        for (int i=1;i<=cnt;i++) ans+=f[n][i][m];
        printf("%ld",ans);
        return 0;
    }
  • 相关阅读:
    java多线程的基本介绍
    Fragment基本介绍
    TypedValue.applyDimension的使用
    获取当前进程名并判断是否是主进程
    Bitmap类、BitmapFactory及BitmapFactory类中的常用方法
    Android 动态改变图片的颜色值
    Glide4.0使用
    Android在一个app中启动另一个App
    使用Recyclerview实现图片水平自动循环滚动
    Java变量的修饰符
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12716230.html
Copyright © 2011-2022 走看看