【Algothrim】 动态规划实例(Stamps)

来源:互联网 发布:在淘宝买狗狗安全吗 编辑:程序博客网 时间:2024/05/16 07:51

Stamps

There are N number kinds of stamps with different worth. At this moment, you are allowed to use the same kinds of stamps several times, but the total number of uses of all stamps is restricted to less than K number.
Within this limitation, you want to know the maximum worth by making the worth one by one starting from 1 using the stamps.
For example, N=2 is given, the worth of each stamp is 1, 3 and K=5 is decided. Then you can make the worth up to 5 by using the stamps whose worth is 1, and you can find it out from 6 to 13 as the below:

6 = 3+3
7 = 3+3+1
8 = 3+3+1+1
9 = 3+3+3
10 = 3+3+3+1
11 = 3+3+3+1+1
12 = 3+3+3+3
13 = 3+3+3+3+1

However, you cannot make 14 using 5 stamps. Therefore, the maximum worth you can make is 13.

Time limit: 1 second (java: 2 seconds)

[Input]
Several test cases can be included in the inputs. T, the number of cases is given in the first row of the inputs. After that, the test cases as many as T (T ≤ 30) are given in a row.
The maximum use number, K and the number of kinds of stamps, N are given by being separated with a blank on the first row per each test case. (1 ≤ N ≤ 50, 1 ≤ K ≤ 200)
Each stamp worth is given by being separated with a blank on the second row. Each worth is a natural number below 10,000.

[Output]
For each test case, you should print "Case #T" in the first line where T means the case number. For each test case, you should output the maximum worth you can when making worth using stamps in order on the first row per each test case.

[I/O Example]

Input
2
5 2
1 3
3 10
29 50 36 43 1 2 4 8 15 22


Output
Case #1

13

Case #2
56


下面这个网址有这道题非常完整的翻译,题解和代码。非常好。

http://www.nocow.cn/index.php/Code:USACO/stamps/C%2B%2B


再贴答案之前,回顾下想这道题的思路。

昨天的帖子,making N dollars, 自以为已经懂了,可以用到这道题目上,写完代码才发现,有1个非常重要的区别,那就是making n dollars里的硬币的个数是无限制的,而这道题目确是有的。

所以一开始想用making N dollars的代码来计算F[n][k](n是总价值,k是硬币种类数,F[n][k]是次数), 那如果F[n][k]是0,不就代表总值为n时是不可达,那就得到所求解啦。

但是这里的k是硬币的种类数,而非硬币的枚数。所以这个想法就是失败的。

既然题中说过受限制的是硬币的枚数,那思路就可以网那边靠了。

我们用F[n]来表示要获得总值为n所需的硬币的最小数。

F[n] 的最小值肯定是f[n-value]+1中最小的那个。value就是硬币的面值。用一个数组遍历即可。

求解出F[n]后,第一个值大于N的就是要找到那个不可达的数,减1就是最终答案。

思路有了,写代码的时候有2个特别要注意的地方。

1)数组的大小,这么题目中写了最大值不超过10000,硬币种类不超过200,所以F是10000*200,可以再放大点

2)n的范围,一开始想都没想就写了value中最大的乘以可用的硬币数量,然后

20 1

1

这个case就返回0,因为都可达。所以最大值要比这个值大1,才能覆盖这个case.

思路就到这里,真是各种坑。

其实还遇到了一个问题,就是循环

for (j = 1; j <= K; j++)
  {
   for (i = 1; i <= max*N + 1; i++)
   {
    if (i >= V[j])
    {
     F[i] = min(F[i - V[j]] + 1, F[i]);
    }
   }
  }

这样写就可以通过测试用例

i 和j 反过来就不行。。。


   for (i = 1; i <= max*N + 1; i++)
   {

for (j = 1; j <= K; j++)
  {

    if (i >= V[j])
    {
     F[i] = min(F[i - V[j]] + 1, F[i]);
    }
   }
  }

这样就一直超时。。。。

弄不明白。


下面贴上答案,据说这道题目可以用BFS写。改天试试。

/*
You should use the statndard input/output

in order to receive a score properly.

Do not use file input and output

Please be very careful.
*/
#include <stdio.h>
#include <string.h>

int Answer;
int N, K;
int V[205];
int F[2000002];

int min(int a, int b)
{
 if (a > b)
  return b;
 else
  return a;
}

int main(void)
{
 int T, test_case;
 /*
 The freopen function below opens input.txt file in read only mode, and afterward,
 the program will read from input.txt file instead of standard(keyboard) input.
 To test your program, you may save input data in input.txt file,
 and use freopen function to read from the file when using scanf function.
 You may remove the comment symbols(//) in the below statement and use it.
 But before submission, you must remove the freopen function or rewrite comment symbols(//).
 */
 // freopen("input.txt", "r", stdin);

 /*
 If you remove the statement below, your program's output may not be rocorded
 when your program is terminated after the time limit.
 For safety, please use setbuf(stdout, NULL); statement.
 */
 setbuf(stdout, NULL);

 scanf("%d", &T);
 for (test_case = 0; test_case < T; test_case++)
 {

  scanf("%d", &N);
  scanf("%d", &K);
  int i, j, t;
  int max = 0;
  for (i = 1; i <= K; i++)
  {
   scanf("%d", &V[i]);
   max = max>V[i] ? max : V[i];
  }

  Answer = 0;
  for (i = 1; i <= max*N + 1; i++)
  {
   F[i] = N + 1;

  }


  for (j = 1; j <= K; j++)
  {
   for (i = 1; i <= max*N + 1; i++)
   {
    if (i >= V[j])
    {
     F[i] = min(F[i - V[j]] + 1, F[i]);
    }
   }
  }


  for (i = 1; i <= max*N + 1; i++)
  {
   if (F[i] > N)
   {
    Answer = i - 1;
    break;
   }
  }
  // Print the answer to standard output(screen).
  printf("Case #%d\n", test_case + 1);
  printf("%d\n", Answer);
 }

 return 0;//Your program should return 0 on normal termination.
}


0 0