POJ1010: 体现搜索艺术的“水”题

问题简述

给出不同类型的一组邮票,类型面值可能相同。求符合总面值,且最多取4张邮票的最佳方案。最佳方案满足以下要求:

  1. 类型数最多。
  2. 如果类型数相同,则张数少者。
  3. 如果张数也相同,则单张面值最大者。
  4. 如果以上都相同,则无最佳方案(平局)。
问题分析

搞了整整3天,绞尽脑汁也没想出个好方案。看了一下关于该题的discussion,许多人说这是个水题,直接暴搜就可以过。可是怎么都想不明白如何暴搜。 最后在网上看到一个利用动态规划法写的一个状态转移方程,顿时豁然开朗,想出了这个方案。关键在于如何递归计算最大类型数,以及判断最优解。

一. 计算最大类型数
用MT[k][n][v] (k>=0, 0<=n<=4, v>=0) 表示从前k个类型中,最多取n张,总面值为v的所有组合中最大的类型数。则有递推方程:

     MT[k][n][v] = MAX{ MT[k-1][n-i][v-i*Values[k-1]] + (i>0?1:0) }  (0<=i<=n, v>=i*Values[k-1])
二. 判断最优解,平局,及无解情况  
<1>每次进行递归之前,先记录当前类型取用的个数 
<2>每次递归到v=0时,即得到一个解,与之前的最优解比较,决定是否更新或平局。 
<3>每次递归到n=0或v=0时, 如果v>0, 则此次递归无解。

基本代码也很简洁:

void searchBestComb(int k, int n, int v)
{
   if(v>0 && n>0 && k>0)
	for(int i=0; i<=n && v>=i*typeValue[k-1]; ++i)
	{
	     typeTimes[k-1] = i;   //use i stamps of this type
	     searchBestComb(k-1, n-i, v-i*typeValue[k-1]);
	}
    else if(v==0) 		
	    updateBestComb(k);   //find an answer
}

提交后一次AC。看了一下ACM的解题报告,终于知道如何暴搜了,虽然效率不高,但也可以过。代码如下:

For(i=1;i<=total_stamps;i++)
   For(j=i;j<=total_stamps;j++)
      For(k=j;k<=total_stamps;k++)
         For(l=k;l<=total_stamps;l++)
           {
              UpdateBestComb();
           }

关键还是我没利用好最多只有4张邮票这个条件,终于知道为什么那么多人说这是个水题了。在被此题困扰的时候我忘了一个基本原则:人类有寻求最简的天性。

反思:

      ACM的题确实有趣,但确实很有挑战。一道题对于经过专业训练的人来说也许是小菜一碟,但对我们这些没受过训练,算法水品一般般,仅凭兴趣研究的人来说可能是一场头脑风暴。苦思冥想几天可能还想不出一个解决方案,即使好不容易憋出一个方案,也可能由于太繁琐而无法实现,或者勉强实现了也错误百出。ACMer不仅仅在基本算法运用方面灵活自如,比如贪心,回溯,动态规划等等,更重要的是,他们更善于用数学符号清晰地表达他们解题思路,比如用一个递推方程,方程写出来后,代码逻辑就清晰多了。我们普通算法爱好者的差距就在这里。这几天有点过分投入了,脑力消耗很大,现在需要心平气和,认清自己的现实情况。也许需要去啃一部《算法导论》之类的名著来打牢基础。无论如何,我会坚持下去。

posted @ 2010-01-30 02:06  head for better  阅读(1796)  评论(0编辑  收藏  举报