USACO Contact, Stamps, 哈希,动态规划

1、contact

这题估计是丢俺老脸的题,因为俺就直接枚举了。唯一的trick是对于 01, 001这样的字符串,计算hash值的时候,需要记得转换为 101, 1001这样的形式,不然肯定就混淆了,所以直接上代码吧,应该好懂。


  1. /*
  2. ID:fairyroad
  3. LANG:C++
  4. TASK:contact
  5. */
  6. #include <fstream>
  7. #include <string>
  8. #include <algorithm>
  9. using  namespace std ;
  10.  
  11. ifstream fin ( "contact.in" ) ;
  12. ofstream fout ( "contact.out" ) ;
  13.  
  14. #define MAXNUM 16385 // 1<<14 + 1
  15. size_t A, B, N ;
  16. string s ;
  17.  
  18. struct Sequence  {
  19.      int cnt ;
  20.     string str ;
  21.     Sequence ( )  : cnt ( 0 ), str ( "" )  { }
  22.     Sequence ( int c, string s )  : cnt (c ), str (s )  { }
  23.      bool operator > ( const Sequence & other )  const  {
  24.          if (cnt  > other. cnt )  return  true ;
  25.          if (cnt  == other. cnt )  {
  26.              if (str. length ( )  < other. str. length ( ) )  return  true ;
  27.              else  if  (str. length ( )  == other. str. length ( ) )  return str  < other. str ;
  28.              else  return  false ;
  29.          }
  30.          return  false ;
  31.      }
  32. } ;
  33.  
  34. Sequence hash [MAXNUM ] ;
  35.  
  36. int main ( )
  37. {
  38.     fin  >> A  >> B  >> N ;
  39.     string line ;
  40.      while (getline (fin, line ) )
  41.         s + =line ;
  42.  
  43.      int num ;
  44.      size_t j, k ;
  45.  
  46.      for ( size_t i  = A ; i  <= B ;  ++i )
  47.      {
  48.          for (=  0  ; j  + i  <= s. size ( ) ;  ++j )
  49.          {
  50.             num  =  1 ; // 注意这里,关键就在这里了
  51.              for (= j ; k  - j  < i ;  ++k )
  52.              {
  53.                 num  <<=  1 ;
  54.                  if (s [k ]  ==  '1' ) num  + =  1 ;
  55.              }
  56.  
  57.              if ( !hash [num ]. cnt )
  58.              {
  59.                 hash [num ]. cnt  =  1 ;
  60.                 hash [num ]. str  = string (s, j, i ) ;
  61.              }
  62.              else
  63.             hash [num ]. cnt  ++ ;
  64.          }
  65.      }
  66.  
  67.     sort (hash, hash  + MAXNUM, greater <Sequence > ( ) ) ; // 可以在前面的循环中就建立一个大小为N的堆
  68.  
  69.      int linenum  =  1, precnt  = hash [ 0 ]. cnt ;
  70.     fout  << hash [ 0 ]. cnt  <<  '\n'  << hash [ 0 ]. str ;
  71.      size_t i, outline  =  1 ;
  72.  
  73.      for (=  1 ; i  < MAXNUM ;  ++i )
  74.      {
  75.          if ( !hash [i ]. cnt )  break ;
  76.  
  77.          if (hash [i ]. cnt  == precnt )
  78.          {
  79.              if (linenum  <  6 )
  80.              {
  81.                 linenum  ++ ;
  82.                 fout  <<  ' '  << hash [i ]. str ;
  83.              }
  84.              else
  85.              {
  86.                 linenum  =  1 ;
  87.                 fout  <<  '\n'  << hash [i ]. str ;
  88.              }
  89.          }
  90.          else
  91.          {
  92.              if (outline  == N )  break ;
  93.             precnt  = hash [i ]. cnt ;
  94.             fout  <<  '\n'  << hash [i ]. cnt  <<  '\n'  << hash [i ]. str ;
  95.             linenum  =  1 ;
  96.             outline ++ ;
  97.          }
  98.      }
  99.  
  100.     fout  << endl ;
  101.      return  0 ;
  102. }

2、Stamps

这题我比较喜欢,我想凡是做过ACM的多少都有点似曾相识的感觉,因为这题多少可以算是背包问题的变种,而背包问题几乎是ACM必修课了,下面直接给出DP公式:

f(m) = 

1. dp(m) if dp(m) is set

2. min(dp(m-s(i)) + 1, for i in [0, n)

因为2中有一个循环,所以复杂度是O(N*M)。


  1. /*
  2. ID:fairyroad
  3. LANG:C++
  4. TASK:stamps
  5. */
  6. #include <fstream>
  7. using  namespace std ;
  8.  
  9. ifstream fin ( "stamps.in" ) ;
  10. ofstream fout ( "stamps.out" ) ;
  11.  
  12. int k, n ;  // 可以使用的邮票的张数,邮票种类
  13. int s [ 50 ] ;
  14. int dp [ 10000 * 200 + 1 ] ;
  15.  
  16. int main ( )
  17. {
  18.     fin  >> k  >> n ;
  19.      int i, min, curr  =  0, tmp ;
  20.      for (=  0 ;< n ;  ++i )
  21.      {
  22.         fin  >> s [i ] ;
  23.         dp [s [i ] ]  =  1 ;  // 这些面值可以直接获取
  24.      }
  25.  
  26.      while ( true )
  27.      {
  28.          ++curr ;
  29.          if (dp [curr ]  ! =  0 )  continue ;  // 注意curr是从1开始升上去的
  30.        
  31.         min  = k + 1 ;
  32.          for ( i  =  0 ; i  < n ;  ++i )
  33.          {
  34.             tmp  = curr -s [i ] ;
  35.              if (tmp  <=  0 )  continue ;   // 输入的邮票面值集合不一定是从小到大排好序的
  36.              if (dp [tmp ]  +  1  < min )
  37.                 min  = dp [curr -s [i ] ]  +  1 ;
  38.          }
  39.  
  40.          if (min  > k )  break ;
  41.  
  42.         dp [curr ]  = min ;
  43.      }
  44.  
  45.     fout  << curr - 1  << endl ;
  46.      return  0 ;
  47. }

老生常谈,大多数情况下,动态规划是缩时利器,虽然空间上会打点折扣。而DP的关键就是要写出那个递推公式。一句八卦就是,如果你熟练掌握了动态规划,大多数情况下,面试中你可以藐视众多面试题~




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值