POJ 2441 Arrange the Bulls(2007-06-07 13:08)

 

两个月前AC过的一道DP题.现在发现了一个复杂度较低的算法了~贴上两次AC的代码比较一下吧.

--------------------------------------以前的--------------------------------------------

以下标i表示barn的每一种状态. 从低位向高位,第k位为1表示第k个barn未被占用,(0<=k<=m-1)

dp[i]=num表示另barn的状态为i的方法有num种

结果就是sum{dp[i]} (0<=i<=(1<<m)-1)

基本上是对每头牛扫描一遍全部状态,判断每个状态的任意一个barn是否可分配.复杂度是n*m*2^m

Problem Id:2441         User Id:ecchi
Memory:4156K         Time:388MS
Language:C++         Result:Accepted

Source

#include "stdafx.h"

#include "stdio.h"
#include "string.h"

int dp[0x100000];
int cow[20];
int op[20];
int n,m;

int main()
{
//    freopen("1.in","r",stdin);
      scanf("%d %d",&n,&m);
      int valid=(1<< m)-1;
      int bn;
      int i,j,k;
      for(i=0;i< m;++i){
          op[i]=1<< i;
      }
      int pos;
      memset(dp,0,0x400000);
      dp[valid]=1;
      memset(cow,0,20);
      for(i=0;i< n;++i){
          scanf("%d",&bn);
          for(j=0;j< bn;++j){
              scanf("%d",&pos);
              cow[i]|=1<< (pos-1);
          }
      }
      int buf;
      int count=0;
      int out;
    for(i=0;i< n;++i){
          for(j=0;j< =valid;++j){    
              buf=j&cow[i];
              if(dp[j]&&buf){
                  for(k=0;k< m;++k){
                      if(buf&op[k]){
                          dp[j^op[k]]+=dp[j];
                      }
                  }
              }
              dp[j]=0;
          }

      }
      dp;
      int sum=0;
      for(i=0;i< =valid;++i){
          sum+=dp[i];
      }
      printf("%d/n",sum);
      return 0;
}

-----------------------------------------现在的--------------------------------------------

以下标i表示barn的每一种状态. 从低位向高位,第k位为1表示第k个barn未被占用,(0<=k<=m-1)

dp[i]=num表示另barn的状态为i的方法有num种

结果就是sum{dp[i]} (0<=i<=(1<<m)-1&& i的二进制位中0的个数为n)

从(1<<m)-1到0,对每一个barn状态算出0的个数,即为当前要处理的牛的号码,再判断任意一个barn是否可以分配.可以看到这样做的复杂度是(2^m)*m (算0的个数时间上<=m),直接少了个n.不过常数会比较大,m,n<=20的情况下,效率不见得会有很大改进.

Problem Id:2441         User Id:ecchi
Memory:4148K         Time:139MS
Language:GCC         Result:Accepted

Source

#include "stdio.h"
#include "string.h"

int dp[0x100000];

int main()
{
//      freopen("1.in","r",stdin);
      int n,m;
      int cow[20];
      int op[20];
      memset(cow,0,80);
      memset(op,0,80);
      scanf("%d %d",&n,&m);
      int valid=(1<< m)-1;
      int bn;
      int i,j,k;
      for(i=0;i< m;++i){
          op[i]=1<< i;
      }
      int pos;
      dp[valid]=1;
      for(i=0;i< n;++i){
          scanf("%d",&bn);
          for(j=0;j< bn;++j){
              scanf("%d",&pos);
              cow[i]|=1<< (pos-1);
          }
      }
      int dpbuf;
      int buf;
      int count;
      int sum=0;
    for(i=valid;i>=0;--i){
          dpbuf=dp[i];
          if(dpbuf==0){
              continue;
          }
          buf=i;
          count=0;
          while(buf>0){
              ++count;
              buf&=(buf-1);
          }
          count=m-count;
          if(count>=n){
              if(count==n){
                  sum+=dpbuf;
              }
              continue;
          }
          buf=i&cow[count];
          if(dpbuf&&buf){
              for(k=0;k< m;++k){
                  if(buf&op[k]){
                      dp[i^op[k]]+=dpbuf;
                  }
              }
          }
      }
      printf("%d/n",sum);
      return 0;
}

-----------------------------------------------总结下------------------------------------------------

较之后面的算法,第一种算法效率较低,在于没有利用好barn状态同时也能反映牛的处理情况的特性.

多了一层n的循环

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值