Project1

标题: 解迷游戏可以用的2-sat谜题核心脚本 [打印本页]

作者: 浮云半仙    时间: 2017-2-14 12:57
标题: 解迷游戏可以用的2-sat谜题核心脚本
本帖最后由 浮云半仙 于 2017-2-14 13:03 编辑

2-sat问题是什么

2sat.zip (1.39 KB, 下载次数: 89)

 
RUBY 代码复制
  1. =begin
  2. 描述:
  3. n个布尔型变量b(0), b(1), ..., b(n-1) 。m个条件,用0表示假,用1表示真。
  4. 每个条件形如
  5.  b(x)为真/假 或者 b(y)为真/假
  6. 求一组符合所有条件的解(注:有可能无解)
  7.  
  8. 这个脚本在游戏里面怎么用呢?
  9. TwoSat.make2sat(n, m) 可以生成一个保证有解的2sat问题,n个bool变量m个条件
  10. TwoSat#check_answer(ans) 给出一组解,检查是否可行。
  11. =end
  12. class TwoSat
  13.   attr_reader :maxn
  14.   attr_accessor :graph
  15.   def initialize(n)
  16.     @maxn = n
  17.     @graph = Array.new(n*2) {[]}
  18.     @dfn = Array.new(n*2, 0)
  19.     @low = Array.new(n*2, 0)
  20.     @scc = Array.new(n*2, 0)
  21.     @scc_cnt = 0
  22.     @dfn_time = 0
  23.     @checked = false
  24.     [url=home.php?mod=space&uid=33409]@Stk[/url] = []
  25.   end
  26.   def add_condition(x, y, xv, yv)
  27.     return if x >= @maxn || y >= @maxn
  28.     @graph[(2*x+xv)^1].push(2*y+yv)
  29.     @graph[(2*y+yv)^1].push(2*x+xv)
  30.   end
  31.   def tarjan(u)
  32.     @dfn_time += 1
  33.     @low[u] = @dfn[u] = @dfn_time
  34.     @stk.push(u)
  35.     @graph[u].each do |to|
  36.       if @dfn[to] == 0
  37.         tarjan(to)
  38.         @low[u] = @low[to] if @low[to] < @low[u]
  39.       elsif @scc[to] == 0
  40.         @low[u] = @dfn[to] if @dfn[to] < @low[u]
  41.       end
  42.     end
  43.     if @low[u] == @dfn[u]
  44.       @scc_cnt += 1
  45.       while @stk.size != 0
  46.         x = @stk.pop
  47.         @scc[x] = @scc_cnt
  48.         break if x == u
  49.       end
  50.     end
  51.   end
  52.   def pre_check
  53.     if !@checked
  54.       (0...@maxn*2).each do |i|
  55.         tarjan(i) if @dfn[i] == 0
  56.       end
  57.       @checked = true
  58.     end
  59.   end
  60.   def answerable?  #检查是否有解
  61.     pre_check
  62.     (0...@maxn).each do |i|
  63.       return false if @scc[i*2] == @scc[i*2+1]
  64.     end
  65.     return true
  66.   end
  67.         #检查答案. #一个长度为n的数组描述bool变量0~n-1的取值
  68.   def check_answer(ans)
  69.     pre_check
  70.     [url=home.php?mod=space&uid=25204]@mark[/url] ||= Array.new(2*@maxn)
  71.     @mark.fill(false)
  72.     ans.each_with_index do |e, i|
  73.       @mark[i*2+e] = true
  74.     end
  75.     (0...@maxn*2).each do |x|
  76.       return false if @mark[x] && !bfs(x)
  77.     end
  78.     return true
  79.   end
  80.   def bfs(x)
  81.     head = 0
  82.     @q ||= Array.new(2*@maxn)
  83.     @vis ||= Array.new(2*@maxn)
  84.     @q[0] = x
  85.     @vis.fill(false)
  86.     @vis[x] = true
  87.     tail = 1
  88.     while(head < tail)
  89.       u = @q[head]; head += 1
  90.       return false if @mark[u] != @mark[x]
  91.       @graph[u].each do |to|
  92.         if !@vis[to]
  93.           @q[tail] = to
  94.           tail += 1
  95.           @vis[to] = true
  96.         end
  97.       end
  98.     end
  99.     return true
  100.   end
  101.  
  102.   def self.make2sat(n, m) #生成一个n个bool变量,m个条件的2-sat问题
  103.     while true
  104.       s = self.new(n)
  105.       cs = []
  106.       m.times do
  107.         x = rand(n)
  108.         y = rand(n)
  109.            #随机构造2-sat
  110.         cs << [x, y, rand(2), rand(2)]
  111.         s.add_condition(*cs.last)
  112.       end
  113.       return [s, cs] if s.answerable? #可解
  114.     end
  115.   end
  116.  
  117. end
  118.  
  119. =begin
  120. s = TwoSat.new(2)                 #两个bool变量
  121. s.add_condition(1, 0, 1, 1)       #添加约束   b(0)为真 || b(1)为真
  122. puts s.answerable?                #是否有解 true
  123. puts s.check_answer([1, 1])       #检查b(0) = 1, b(1) = 1是否可行 true
  124. puts s.check_answer([1, 0])       #检查b(0) = 1, b(1) = 0是否可行 true
  125. puts s.check_answer([0, 1])       #检查b(0) = 0, b(1) = 1是否可行 true
  126. puts s.check_answer([0, 0])       #检查b(0) = 0, b(1) = 0是否可行 false
  127. =end





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1