USACO算法系列七——stamps

     题目:http://www.nocow.cn/index.php/Translate:USACO/stamps

    邮票数可以显示最大连接自然数的问题,看到这道题目第一个想到的是整数划分问题。但是又很不一样。第一它不用考虑这个整数的所有划分情况,而是只要考虑整数是否可以被<=K个邮票表示。

    最简单的想法,用stamp【N】数组表示邮票,然后递增考虑1到K*stamp【N】范围的自然数的划分,直到不能划分为止。为了加速我们可以对邮票面值先做一下排序,但是还是超时了。代码如下:

 #include<iostream> #include<fstream> #include <algorithm> #define MAX 50 #define KMAX 201 using namespace std; int k,n; int stamp[MAX]; bool isleagle(int num,int index,int s) { if (index < 0) { return false; } if (num == 0) { return true; } else { for (int i=s; i >= 0; i --) { if (num > stamp[i] * index) { break; } if (num < stamp[i]) { continue; } if(isleagle(num-stamp[i],index-1,i)) { return true; } } return false; } } int main() { ifstream fin("stamps.in"); ofstream fout("stamps.out"); //输入数据 fin >> k >> n; for (int i=0; i < n; i ++) { fin >> stamp[i] ; } //排序 sort(stamp,stamp+n); //判断获取结果 int resut = -1; int large = k * stamp[n-1]; for (int i=1; i <= large; i ++) { if (!isleagle(i,k,n-1)) { resut = i; break; } } //打印结果 if (resut == -1) { fout<< large<< endl; } else fout<< resut-1 << endl; return 0; }

 

     很可惜的是,在TEST11的时候,有一点小的超时。

Executing... Test 1: TEST OK [0.000 secs, 3016 KB] Test 2: TEST OK [0.011 secs, 3016 KB] Test 3: TEST OK [0.000 secs, 3016 KB] Test 4: TEST OK [0.000 secs, 3016 KB] Test 5: TEST OK [0.011 secs, 3016 KB] Test 6: TEST OK [0.000 secs, 3016 KB] Test 7: TEST OK [0.011 secs, 3016 KB] Test 8: TEST OK [0.011 secs, 3016 KB] Test 9: TEST OK [0.011 secs, 3016 KB] Test 10: TEST OK [0.875 secs, 3016 KB] > Run 11: Execution error: Your program (`stamps') used more than the allotted runtime of 3 seconds (it ended or was stopped at 3.672 seconds) when presented with test case 11. It used 3016 KB of memory. ------ Data for Run 11 ------ 200 50 1 37 87 14 137 4016 157 5383 7318 8662 7074 2821 5250 9704 8986 1375 6587 5962 7190 339 1981 9719 7012 385 7926 5159 3259 5144 7846 8854 3249 3198 8408 2784 6249 4648 6799 2757 31 4116 7771 3456 3288 3020 3159 8625 747 9745 938 10000 ---------------------------- Test 11: RUNTIME 3.672>3 (3016 KB)

 

    但是仔细考虑一下这道题目,发现这道题实际上可以用动态规划的思想来解决。

    动态规划的题目,用F[i]表示得到i面值所需要的最少邮票个数,Value[j](j=1..Stamps)表示每个邮票的面值,可得以下转移方程:

                  F[i]=Min ( F[i-Value[j]] + 1 ) (i-Value[j]>=0 j=1..Stamps)

    初始状态:F[0]=0;F[i]=INFINITE;

    如果计算中间某个F[i]大于了最大可以使用的邮票数量,则退出循环,输出i-1即为解。----引自USACO解答

    这样子的话,这道题目就不难解决了。代码如下:

#include<iostream> #include<fstream> #include <algorithm> #define MAX 50 #define KMAX 201 using namespace std; int k,n; int stamp[MAX]; short point[2000000] = {0}; //备忘录表 int isleagle(int num) { int min = -1; for (int i=n-1; i >= 0; i --) { if (num < stamp[i]) { continue; } int temp = point[num-stamp[i]] +1; if (temp <= k && (min > temp || min ==-1)) { min= temp; } } point[num] = min; return min; } int main() { ifstream fin("stamps.in"); ofstream fout("stamps.out"); //输入数据 fin >> k >> n; for (int i=0; i < n; i ++) { fin >> stamp[i] ; } //排序 sort(stamp,stamp+n); //判断获取结果 int resut = -1; int large = k * stamp[n-1]; for (int i=1; i <= large; i ++) { if (isleagle(i) == -1) { resut = i; break; } } //打印结果 if (resut == -1) { fout<< large<< endl; } else fout<< resut-1 << endl; return 0; }

     运行时,顺利通过了,只不过内存翻了一倍。

Executing... Test 1: TEST OK [0.011 secs, 6916 KB] Test 2: TEST OK [0.000 secs, 6916 KB] Test 3: TEST OK [0.000 secs, 6916 KB] Test 4: TEST OK [0.000 secs, 6916 KB] Test 5: TEST OK [0.000 secs, 6916 KB] Test 6: TEST OK [0.000 secs, 6916 KB] Test 7: TEST OK [0.000 secs, 6916 KB] Test 8: TEST OK [0.011 secs, 6916 KB] Test 9: TEST OK [0.000 secs, 6916 KB] Test 10: TEST OK [0.097 secs, 6916 KB] Test 11: TEST OK [0.907 secs, 6916 KB] Test 12: TEST OK [0.205 secs, 6916 KB] Test 13: TEST OK [0.032 secs, 6916 KB] All tests OK.

    这道题上USACO上,还提供了BFS算法,速度更快。大家可以到官网上去好好看一下。

你可能感兴趣的:(USACO算法系列七——stamps)