2-sat(2)随机算法

下面我们来说说algo2里提到的随机算法。

在讲随机算法之前,首先要介绍“random walks on a line”,假设我们在数轴的点0的位置,在点0的话,下一个时刻我们会移动到点1上,如果在其他的点x,我们有50%的概率下一个时刻移动到点x-1上,有50%的概率移动到x+1上。现在让我们来算一下,从点0开始移动到点n,期望需要移动多少步。这个期望的算法和之前写的这篇的方法差不多。设E(i)是从点i(i<=n)移动到点n所需要的期望的步数。于是我们有

E(n)=0
E(0)=E(1)+1
E(i)=0.5(1+E(i+1))+0.5(1+E(i1))

有上面的式子我们可以得到最后的结果,推导比较简单,就不赘述了

E(0)=n2

设Tn表示从0走到n的步数,于是我们有

n2=E(Tn)=2n2k=0(kP(Tn=k))+k=2n2+1(kP(Tn=k))
因为 2n2k=0(kP(Tn=k))0
所以 n2k=2n2+1(kP(Tn=k))
k=2n2+1((2n2)P(Tn=k))
=2n2P(Tn>2n2)

于是我们得到

P(Tn>2n2)12

也就是说我们从0开始走2*n*n步,我们有大于1/2的概率已经经过n这个点了。

ok回到我们的2-sat问题。

我们的算法是这样的,我们开始时随机给变量赋值0或者1,然后迭代足够多的次数(大于2*n*n的次数),如果某次迭代,我们找到了答案就可以返回结果了,如果我们迭代足够多的次数都没找到答案就返回无解。每次迭代,我们找一个不满足要求的子式,然后两个变量随机挑一个改变他的值。

假设我们现在的解是a,满足要求的解是a*,那么我们的解和满足要求的解,值不同的变量的个数是x,某次迭代,我们找到一个子式(xi or xj)不满足条件,因为子式不满足条件,所以xi和xj不可能同时和a*里值是相同的,如果两个值都不同,那么我们这次改动使得x=x-1,如果有一个值不同,那么我们有50%的概率使得x=x-1有50%的概率使得x=x+1,所以,答案变好的概率要大于50%,所以迭代2*n*n次能得到解的概率就会小于1/2。

很有趣的算法,并且我们用这个算法,可以AC掉poj 3648这道题!看代码

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <cstring>
  4. #include <ctime>
  5.  
  6. const  int maxn = 10010;
  7. int n, m, a, b;
  8. char c1, c2;
  9. int ans [maxn ], record [maxn ] [ 2 ];
  10.  
  11. int get ( int x ) {
  12.      if (x<n )
  13.          return ans [x ];
  14.      return  !ans [x -n ];
  15. }
  16. int main ( ) {
  17.      //srand(time(NULL));这里如果调用time,poj会报 runtime error的错误
  18.     srand ( 7 );
  19.      while ( 1 ) {
  20.         scanf ( "%d%d"&n,  &m );
  21.         memset (ans,  0sizeof ans );
  22.          if ( !n &&!m )
  23.              break;
  24.          for ( int i = 0; i<m;  ++i ) {
  25.             scanf ( "%d%c%d%c"&a,  &c1,  &b,  &c2 );
  26.              if (c1 == 'w' )
  27.                 a  += n;
  28.              if (c2 == 'w' )
  29.                 b  += n;
  30.             record [i ] [ 0 ]  = a;
  31.             record [i ] [ 1 ]  = b;
  32.          }
  33.          int mark  =  0;
  34.          for ( int i = 0; i< 5 *n *n;  ++i ) {
  35.             mark  =  1;
  36.              for ( int j = 0; j<m;  ++j )
  37.                  if ( (record [j ] [ 0 ] %n !=record [j ] [ 1 ] %n ) &&! (get (record [j ] [ 0 ] )||get (record [j ] [ 1 ] ) ) ) {
  38.                      int tmp  = rand ( ) % 2;
  39.                      if (record [j ] [ 0 ] %n == 0 )
  40.                         tmp  =  1;
  41.                      if (record [j ] [ 1 ] %n == 0 )
  42.                         tmp  =  0;
  43.                     ans [record [j ] [tmp ] %n ]  =  1 -ans [record [j ] [tmp ] %n ];
  44.                     mark  =  0;
  45.                      break;
  46.                  }
  47.              if (mark )
  48.                  break;
  49.          }
  50.          if ( !mark ) {
  51.              printf ( "bad luck\n" );
  52.              continue;
  53.          }
  54.          for ( int i = 1; i<n;  ++i ) {
  55.              if (ans [i ] )
  56.                  printf ( "%dh", i );
  57.              else
  58.                  printf ( "%dw", i );
  59.              if (i<n -1 )
  60.                  printf ( " " );
  61.              else
  62.                  printf ( "\n" );
  63.          }
  64.      }
  65.      return  0;
  66. }

记得之前gcj曾经有道题目需要用到生日悖论来解决,感觉和这个随机算法都是很有趣的算法阿。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值