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

本文深入探讨了动态规划在解决邮票问题中的应用,通过提供详细的DP公式和代码实现,展示了如何高效求解最优解。文章首先介绍了动态规划的基本概念及其在背包问题变种中的应用背景,接着详细阐述了解决邮票问题的动态规划思路,包括状态转移方程的设定和边界条件的处理,并附带了具体的C++代码实例。最后,文章总结了动态规划技巧在实际编程中的价值,强调了其在解决复杂问题时的优越性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的关键就是要写出那个递推公式。一句八卦就是,如果你熟练掌握了动态规划,大多数情况下,面试中你可以藐视众多面试题~




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值