USACO Stringsobits, 还是得搬出动态规划来

这题证明俺现在真是老了,退步了,这是第一份代码:

  1. #include
  2. using  namespace std ;
  3.  
  4. ifstream fin ( "kimbits.in" ) ;
  5. ofstream fout ( "kimbits.out" ) ;
  6.  
  7. unsigned  int N, L, pos ;
  8.  
  9. int main ( )
  10. {
  11.     fin  >> N  >> L  >> pos ;
  12.  
  13.      unsigned  int num  =  0, count  =  0, max  =  ( 1 <<N ) - 1, bit1, tmp ;
  14.      while (count  < pos )
  15.      {
  16.          if (num  > max )  break ;
  17.         bit1  =  0, tmp  = num ;
  18.          for ( ; tmp  >  0 ; bit1 ++ )
  19.             tmp  & =  (tmp  -  1 ) ;
  20.          if (bit1  <= L )  ++count ;
  21.          ++num ;
  22.      }
  23.  
  24.     num  --  ;
  25.      for  ( int i  = N - 1 ; i  >= 0 ;  --i )
  26.         fout  <<  ( (num  >>i )  & 0x1u ) ;
  27.     fout  << endl ;
  28.      return  0 ;
  29. }

其实还是挺简洁的哈,就是会超时,因为基本思路是暴力搜索。


这个是很郁闷的,看数据,确实太大了。仔细端详一番,觉得有DP的感觉,例如,对于(二进制)二位数:10 11,要得到三位数,实际上就是移位和移位+1,+1的操作中可以判断+1后得到的数字中的1是不是超过了给定的L,不过,首先还是先崩溃了一下:

  1. #include
  2. #include
  3. #include
  4. using  namespace std ;
  5.  
  6. ifstream fin ( "kimbits.in" ) ;
  7. ofstream fout ( "kimbits.out" ) ;
  8.  
  9. typedef  unsigned  int UINT ;
  10. UINT N, L, pos ;
  11. vector <deque <UINT >  > dp ;
  12.  
  13. inline  int printBinary (UINT num )
  14. {
  15.      for  ( int i  = N - 1 ; i  >= 0 ;  --i )
  16.         fout  <<  ( (num  >>i )  & 0x1u ) ;
  17.     fout  << endl ;
  18.      return  0 ;
  19. }
  20.  
  21. int main ( )
  22. {
  23.     fin  >> N  >> L  >> pos ;
  24.  
  25.      if (pos  <=  2 )  {
  26.         printBinary (pos - 1 ) ;
  27.          return  0 ;
  28.      }
  29.  
  30.     deque <UINT > d, dPre ;
  31.     d. push_back ( 0 ) ;
  32.      for (UINT i  =  0 ; i  <= N ;  ++i )
  33.         dp. push_back (d ) ;
  34.  
  35.     dp [ 0 ] [ 0 ]  =  1,  dp [ 0 ]. push_back ( 1 ) ;
  36.     UINT count  =  2, uTmp ;
  37.  
  38.      for (UINT i  =  0 ; i  < N ;  ++i )
  39.      {
  40.          for (UINT k  =  0 ; k  <= i ;  ++k )
  41.          {
  42.             d. clear ( ) ;
  43.              for (UINT j  =  1 ; j  <= dp [k ] [ 0 ] ;  ++j )
  44.              {
  45.                 uTmp  =  (dp [k ] [j ]  <<  1 ) ;
  46.                  if ( ++count  == pos )  return printBinary (uTmp ) ;
  47.  
  48.                  if (k + 2  <= L )  // k从0开始的
  49.                  {
  50.                     uTmp  =  (dp [k ] [j ] << 1 ) + 1 ;
  51.                      if ( ++count  == pos )  return printBinary (uTmp ) ;
  52.                     d. push_back (uTmp ) ;
  53.                  }
  54.  
  55.                 dp [k ] [j ]  <<=  1 ;
  56.              }
  57.              if (== i )
  58.              {
  59.                 dp [k + 1 ]. insert ( ++dp [k + 1 ]. begin ( ), d. begin ( ), d. end ( ) ) ;
  60.                 dp [k + 1 ] [ 0 ]  + = d. size ( ) ;
  61.              }
  62.              if (>  0 )
  63.              {
  64.                 dp [k ]. insert ( ++dp [k ]. begin ( ), dPre. begin ( ), dPre. end ( ) ) ;
  65.                 dp [k ] [ 0 ]  + = dPre. size ( ) ;
  66.              }
  67.             dPre  = d ;
  68.          }
  69.      }
  70.  
  71.      return  0 ;
  72. }

时间还不知道,空间先扛不住了:


看Test 6和7,估计这个办法也不咋滴,为什么呢,究其原因还是无效计算过多。下面是AC的代码,主要算法思想写在注释里了,这道题本来很简单的,却搞了我这么久,受打击了,呜呜....

  1. #include
  2. using  namespace std ;
  3. typedef  unsigned  int UINT ;
  4. ifstream fin ( "kimbits.in" ) ;
  5. ofstream fout ( "kimbits.out" ) ;
  6.  
  7. UINT N, L, pos ;
  8. UINT dp [ 33 ] [ 33 ] ;  // dp(i,j)表示长度为i,1的个数不超过j的串有多少
  9. int res [ 33 ] = { 0 } ;
  10.  
  11. int main ( )
  12. {
  13.     fin >> N  >> L  >> pos ;
  14.      for (UINT j  =  0 ; j  <= L ; j ++ )
  15.         dp [ 0 ] [j ]  =  1 ;
  16.    
  17.      // 方程:f[j,k]=f[j-1,k]+f[j-1,k-1]; 分别表示在当前位加上0和加上1时的两种状况
  18.      // 边界:f[j,0]=1, f[0,j]=1, f[j,k](k>j)=f[j,j]
  19.      // 这样我们得到了所有的f[j,k] 需要做的就是据此构造出所求字符串
  20.      for (UINT i  =  0 ; i  <= N ; i ++ )
  21.      {
  22.          for (UINT j  =  0 ; j  <=L ; j ++ )
  23.          {
  24.              if (==  0 ) dp [i ] [j ]  =  1 ;
  25.              else  if (<= i ) dp [i ] [j ]  = dp [i - 1 ] [j ]  + dp [i - 1 ] [j - 1 ] ;
  26.              else  if (> i ) dp [i ] [j ]  = dp [i ] [i ] ;
  27.          }
  28.      }
  29.    
  30.      // 构造思路如下:
  31.      // 设所求串为S,假设S的位中最高位的1在K位
  32.      // 那么必然满足:
  33.      // F[K-1,L]=pos
  34.      // 这样的K是唯一的, 所以S的第一个1在从右至左第K位
  35.      // 因为有F[K-1,L]个串第K位上为0,所以所求的第I个数的后K位就应该是:
  36.      // 满足"位数为K且串中1不超过L-1个"这个条件的第 pos - F[K,L] 个数 => 递归的过程
  37.      while (pos  >  1 )
  38.      {
  39.          for ( int i  = N - 1 ; i >= 0 ; i -- )
  40.          {
  41.              if (dp [i ] [L ]  < pos )
  42.              {
  43.                 res [N -i ]  =  1 ;
  44.                 pos  - = dp [i ] [L ] ;
  45.                  break ;
  46.              }
  47.          }
  48.         L -- ;
  49.      }
  50.     UINT i  =  1 ;
  51.      while (res [i ]  ==  0  &&  33  - i  <= N ) i ++ ;
  52.      if (== N + 1 ) i  =  1 ;
  53.      for ( ; i  <= N ; i ++ )
  54.         fout  << res [i ] ;
  55.     fout  << endl ;
  56.      return  0 ;
  57. }



你可能感兴趣的:(USACO,算法,ini)