zoukankan      html  css  js  c++  java
  • BZOJ 1087 [SCOI2005]互不侵犯King 【状压dp】

    BZOJ 1087 [SCOI2005]互不侵犯King

    Description

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

    Input

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

    Output

      方案数。

    Sample Input

    3 2

    Sample Output

    16
     
     
    题解:
    题目描述非常简洁,做起来。。。。
    这道题一开始想---搜索,可是会T。或者组合数?发现推不出。。。
    好吧,那还是试试状压 dp 吧。。。
    对于这类棋盘类问题,一般都要 状压 。
    一开始想,如果要记录全盘状态,存不下啊!!!后来经 dalao  指点,恍然大悟,当前行只与上一行有关系,因为它的范围只有附近八个格子。。(诶,是我太弱了)
    这样我们设 dp[i][j][k] 为做到第 i 行,共放了 j 个王,第 i 行的状态为 k。
    这样一来我们就好推了。 dp[i][j][k]= dp[i-1][j-num[i]][last]  (num[i] 表示第 i 行放多少个王,last 表示第 i-1 行的状态)
    还剩下一个问题:怎么判断合不合法,能不能放。
    这里就要用到强大的位运算技巧了。。。%dalao (状压 dp 题,位运算一定要熟练掌握啊!!!)
    判断同一行相邻两个是否合法:只需要 i & (i<<1) ,这样错开来,如果为1就说明在某一位置,它的两边有王,不合法;
    判断上下两行的话就要两次了:既要 i<<1 ,又要 i>>1 (因为两行状态不同,所以左右两边都要判一下,同一行的话往左移,往右移效果是一样的)
     
    贴代码:
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,k,num[1005];
     4 long long dp[10][1005][1005];
     5 bool flag[1005];
     6 long long ans;
     7 int main()
     8 {
     9     scanf("%d%d",&n,&k);
    10     for (int i=0; i<(1<<n); i++)
    11       if (!(i&(i<<1)))
    12       {
    13           flag[i]=true;
    14           int t=i;
    15           while (t)
    16           {
    17               num[i]+=(t&1);
    18               t>>=1;
    19           }
    20           dp[1][num[i]][i]=1;
    21       }
    22     for (int i=2; i<=n; i++)
    23       for (int j=0; j<=k; j++)
    24         for (int now=0; now<(1<<n); now++)
    25         {
    26             if (!flag[now]) continue;
    27             if (num[now]>j) continue;
    28             for (int last=0; last<(1<<n); last++)
    29             {
    30                 if (!flag[last]) continue;
    31                 if ((last & now) || ((now<<1)&last) || ((now>>1)&last)) continue;
    32                 dp[i][j][now]+=dp[i-1][j-num[now]][last];
    33             }
    34         }
    35     for (int i=0; i<(1<<n); i++)
    36       ans+=dp[n][k][i];
    37     cout<<ans<<endl;
    38     return 0;
    39 }
    View Code

    dp还是欠缺啊!!!要多练啊!!!dp很重要!!!

    加油加油加油!!!fighting fighting fighting!!!

  • 相关阅读:
    tuple 元组及字典dict
    day 49 css属性补充浮动 属性定位 抽屉作业
    day48 选择器(基本、层级 、属性) css属性
    day47 列表 表单 css初识
    day 46 http和html
    day 45索引
    day 44 练习题讲解 多表查询
    day 40 多表查询 子查询
    day39 表之间的关联关系、 补充 表操作总结 where 、group by、
    day38 数据类型 约束条件
  • 原文地址:https://www.cnblogs.com/Frank-King/p/9334786.html
Copyright © 2011-2022 走看看