学校测试-2015-03-01

记得以前做N皇后问题见到过二进制+位运算优化的方法, 今天的搜索题第三题和第四题都可以用到二进制和位运算. 就只做了这两个题目.


题目三

描述

传递游戏(pass)

Description
n个人在做传递物品的游戏,编号为1-n。
游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。
即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;
求当物品经过所有n个人后,整个过程的最小总代价是多少。
Input Format
第一行为n,表示共有n个人(16>=n>=2);
以下为n*n的矩阵,第i+1行、第j列表示物品从编号为i的人传递到编号为j的人所花费的代价,特别的有第i+1行、第i列为-1(因为物品不能自己传给自己),其他数据均为正整数(<=10000)。
Output Format
一个数,为最小的代价总和。


分析

  • 状压DP, 二进制表示状态(点到过或没到过), 位运算实现查找和更新等操作
  • f[i][k]表示状态为 k 并且最后停留在 i 点时的最小代价
  • => min{f[i][(1<<n)+1], (0<=i<n)}
  • 转移 :
    枚举状态 k : 0 -> (1 << n) - 1
    枚举要更新的结点 i : 0 -> n-1
    枚举中间结点 j : 0 -> n-1, (i != j)
    如果满足 在状态 k 中第 i 的位置为 0, 而第 j 的位置为 1, 那么就可以用 j 去更新 i.
  • 核心 :

      if(i != j && (k&(1 << j)) && !(k&(1 << i))) 
                f[i][k^(1 << i)] = min(f[i][k^(1 << i)], f[j][k] + d[j][i]);
  • 初始化
    f[i][1 << i] = 0, (0<=i<n)
    表示从任意点出发


代码

https://code.csdn.net/snippets/609746


题目四

描述

皇后守卫(queen)
Description
给一个N * M的棋盘,棋盘上的有些格子被打上了标记。现在需要在其中放置尽量少的皇后,使得所有被打上标记的格子至少被某一个皇后攻击或占据到。皇后之间可以互相攻击。
Input Format
输入最多15组数据。
每组数据第一行包含两个整数N和M(1 < N, M < 10),以下为一个N行M列的棋盘,其中打上标记的格子用‘X’表示,其它格子用‘.’表示。
输入以一个0结尾。
Output Format
对于每组数据,输出一个数表示最少需要使用的皇后数目。


分析

  • 二进制+位运算优化+普通最优化剪枝的 dfs
  • void dfs(int x, int row, int ld, int rd, int* k, int c)
    x => 当前行
    k => 数组, 表示当前所有被覆盖的位置, 每一行都是用二进制表示的, 所以数组只需开一维
    c => 计数变量, 已经放的皇后数
    主函数调用dfs(0, 0, 0, 0, k, 0) // k 初始化全为 0
  • 函数执行过程
    如果该状态下满足了需要, 直接更新答案后返回
    如果所以行已经考虑完了(x >= n), 直接返回
    最优化剪枝 : 如果当前 c 不比已经记录的 ans 更优, 直接返回
    从当前行中选可以放置皇后的地方开始放皇后, 继续dfs到下一层
    该行不放皇后, dfs到下一层
  • 许多精巧的位运算 :

    • 判断是否满足条件, 标记的地方都被覆盖到
      枚举 x, 如果所有 (k[x]&tar[x]) == tar[x] 说明满足
      解释 : tar[x]表示第 x 行需要被覆盖的二进制状态. 只有当 tar[x] 为 1 的地方 k[x] 也为 1 才满足
    • 更新状态, 在第 x 行二进制下 p 位置放置皇后对原可覆盖状态 k 的影响
    void update(int x, int p, int* k)
    {
    k[x] = upperlim;
    for(int i = 1; x-i >= 0; i++) {
        if((p<<i) < upperlim) k[x-i] |= (p<<i);
        if((p>>i) > 0) k[x-i] |= (p>>i);
        k[x-i] |= p;
    }
    for(int i = 1; x+i < n; i++) {
        if((p<<i) < upperlim) k[x+i] |= (p<<i);
        if((p>>i) > 0) k[x+i] |= (p>>i);
        k[x+i] |= p;
    }
    }

    解释 : upperlim = (1 << n) - 1
    首先 x 行应该全变为 1, 也就是 k[x] 状态为 upperlim
    再考虑对角线和列
    对p的解释 : p 的二进制中只有一位为 1, 也就是皇后所在列的那一位

  • 其他
    • ans 可以初始化为 5
      在 9*9 全 ‘X’ 的棋盘上
    • 皇后可以互相攻击, 也就是同一行上可以有多个皇后

代码

https://code.csdn.net/snippets/609744



posted @ 2015-03-01 14:15  wfwbz  阅读(181)  评论(0编辑  收藏  举报