zoukankan      html  css  js  c++  java
  • 【状态压缩DP】BZOJ1087-[SCOI2005]互不侵犯King

    【题目大意】

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

    【思路】

    先预处理每一行可行的状态(即单行中左右没有相邻的1),存放到usable中。

    然后预处理usable中两两之间能否相互转换,存放到map中。

    f[i][j][k]第i行,已用去j个国王,当前行状态为usable[k]的情况,四重循环暴力递推即可!

    这里用了usable按道理应该会快一些,但是和popoqqq的速度是一样的?不过这个优化应该是有效的!

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 typedef long long ll;
     7 const int MAXN=512;
     8 const int MAXK=100;
     9 const int MAXM=15;
    10 int n,m;
    11 int map[MAXN][MAXN];
    12 ll f[MAXM][MAXK][MAXN];//f[i][j][k]第i行,已用去j个国王,当前行状态为k 
    13 int usable[MAXN];
    14 int digit[MAXN];
    15  
    16 int Usable(int x)
    17 {
    18     if (x<<1&x || x>>1&x) return 0;else return 1;
    19     //不能有相邻的1 
    20 }
    21  
    22 int get_digit(int x)
    23 {
    24     int ret=0;
    25     while (x) ret+=x&1,x>>=1;
    26     return ret;
    27 }
    28  
    29 int Judge(int x,int y)
    30 {
    31     if (x&y || (x<<1)&y || (x>>1)&y) return 0;else return 1;
    32     /*不可行的情况有三种*/
    33 }
    34  
    35 void init()
    36 {
    37     memset(usable,0,sizeof(usable));
    38     memset(map,0,sizeof(map));
    39     for (int i=0;i<1<<n;i++)
    40         if (Usable(i)) usable[++usable[0]]=i;
    41     /*预处理可行的状态(左右不能有相邻的1)*/
    42     for (int i=1;i<=usable[0];i++)
    43         for (int j=1;j<=usable[0];j++)
    44         { 
    45             if (Judge(usable[i],usable[j])) map[i][j]=1;
    46         } 
    47     /*预处理可行的状态中能够转换的状态*/
    48     for (int i=1;i<=usable[0];i++) digit[i]=get_digit(usable[i]);
    49     /*预处理每一个可行状态中1的个数*/
    50 }
    51  
    52 ll dp()
    53 {
    54     memset(f,0,sizeof(f));
    55     f[0][0][1]=1;
    56     for (int i=1;i<=n;i++)
    57         for (int j=0;j<=m;j++)
    58             for (int k=1;k<=usable[0];k++)
    59                 if (digit[k]<=j)
    60                 {
    61                     for (int l=1;l<=usable[0];l++)
    62                     {
    63                         if (map[l][k] && digit[l]+digit[k]<=j) 
    64                             f[i][j][k]+=f[i-1][j-digit[k]][l];
    65                     }
    66                 }
    67     ll ret=0;
    68     for (int i=1;i<=usable[0];i++) ret+=f[n][m][i];
    69     return ret;
    70 }
    71  
    72 int main()
    73 {
    74     scanf("%d%d",&n,&m);
    75     init();
    76     cout<<dp()<<endl;
    77     return 0;   
    78 } 
  • 相关阅读:
    利用Clojure统计代码文件数量和代码行数
    Workflow:添加工作流存储功能
    MongoDB:最简单的增删改查(Oops,可能太简单了)
    《WF in 24 Hours》读书笔记
    推荐一个学习python的网站
    Inter系列处理器名称浅析
    [Android1.5]TextView跑马灯效果
    Code::Blocks 的配色方案
    PuTTY + Xming 远程使用 Linux GUI
    Linux下查看文件和文件夹大小
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5645057.html
Copyright © 2011-2022 走看看