P1896 [SCOI2005]互不侵犯

知识点: 状压DP

原题面

发现 \(n,m \le9\) ,又要求方案数 ,
就知道这必然是个状压了.

分析题意:

一些性质:

  1. 对于每一行 的棋子摆放情况 , 只会被相邻的两行棋子所影响

  2. 对于两行棋子能否相邻 , 处理方式较麻烦
    但是 \(n,m\le 9\) , 状态数较少 ,
    可以预处理出所有单行 状态 , 及他们能相邻 的 其他单行状态

算法实现:

先预处理出 单行 的 合法状态 . (即没有 左右相邻 的 国王的状态)
并记录此单行状态 的 国王个数 .
再预处理出 能直接相邻 的单行状态 . (检查每个国王相邻 \(8\) 格内是否有其他国王)

之后开始状压 \(DP\)
\(f[i][j][k]\) 表示 , 第 \(i\) 行状态为 \(j\) , \(1~i\) 行已经放置了 \(k\) 个国王的方案数
三层循环枚举 当前行数 \(i\) , 第 \(i-1\) 行的状态 , 第 \(i\) 行的状态
然后 检查枚举的两行状态是否 能够相邻.

如果可以相邻 , 继续枚举 第 \(1~i\) 行的国王数量,
再用 \(i-1\) 行的方案数 更新 第i行的方案数

转移方程式:
\(f[i][j][k] = \sum f[i-1][l][k-num[l]]\)
(\(l\)\(j\) 是可以相邻的合法状态 , \(num\) 记录每个单行状态的国王数量)

一些小技巧:

记录合法状态时 ,可以使用 \(vector\) 类.
直接将合法状态压入 \(vector\) 中,
枚举状态时 , 用状态在 \(vector\) 中的编号 映射 状态,
以避免不必要的枚举 , 浪费时间空间 , 增长代码量.

#include
#include
#include
#define max(a,b) ((a)>(b)?(a):(b))
#define lowbit(x) ((x)&(-x))
//=============================================================
int n,k , all,size ,quan[520];
std::vector  plan;//存单行合法状态 
bool map[520][520];//记录两状态能否相邻 
long long ans,f[15][100][520]; 
//=============================================================
inline int read()
{
    int s=1, w=0; char ch=getchar();
    for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
    for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
    return s*w;
}
bool check(int x,int y)//检查x,y两状态能否相邻 
{
    while(x && y)
    {
      //如果x相邻三位中 , 能与y的一位同为1 , 那么不合法 
      if(((x>>1)&y) || ((x<<1)&y) || (x&y)) return 0;
      x>>=1,y>>=1; 
    }
    return 1;
}
void prepare()
{
    n=read(),k=read(); all=(1<>1)) {flag=1;break;}
      if(flag || sum>k) continue;//有相邻国王,或者单行数量>总数量 , 不合法 
      
      plan.push_back(i);//加入vector中 
      f[1][sum][plan.size()-1]=1;//初始化i=1时的答案 
      quan[plan.size()-1]=sum;//记录数量 
    }
    
    size=plan.size();//检查各状态能否相邻 
    for(int i=0; i

你可能感兴趣的:(P1896 [SCOI2005]互不侵犯)