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!!!

  • 相关阅读:
    Xilinx之软件平台 ISE与Vivado前世今生
    博客开园
    第一天:开始你的Jobeet项目
    MySQL之alter语句用法总结
    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句
    MySQL中distinct和group by性能比较[转]
    GROUP BY,WHERE,HAVING之间的区别和用法
    split(),preg_split()与explode()函数分析与介
    解析posix与perl标准的正则表达式区别
    sql关键字的解释执行顺序
  • 原文地址:https://www.cnblogs.com/Frank-King/p/9334786.html
Copyright © 2011-2022 走看看