【算法】【动态规划】 邮票问题

题目描述:

 已知一个 N 枚邮票的面值集合(如,{1 分,3 分})和一个上限 K —— 表示信封上能够贴 K 张邮票。计算从 1 到 M 的最大连续可贴出的邮资。 

例如,假设有 1 分和 3 分的邮票;你最多可以贴 5 张邮票。很容易贴出 1 到 5 分的邮资(用 1 分邮票贴就行了),接下来的邮资也不难: 
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

然而,使用 5 枚 1 分或者 3 分的邮票根本不可能贴出 14 分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13。

输入:

第 1 行: 两个整数,K 和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。 
第 2 行至末尾: N 个整数,每行 15 个,列出所有的 N 个邮票的面值,面值不超过 10000。

输出:

一个整数,从 1 分开始连续的可用集合中不多于 K 张邮票贴出的邮资数。

样例输入:

5 2
1 3

样例输出:

13


分析:

1.数据分析:每个信封最多贴K(K<=200)张邮票,每张邮票的面值不超过10000,能贴出最大的邮资不超过2000000,可用一个数组来表示能够表示贴出每种邮资。


2.算法分析:

(1)搜索:每种邮票最多贴200张,总共50种,朴素的深搜规模将达到50^200。

(2)动态规划:

        <1>阶段:能够构成每个面值为阶段。比如能构成的面值为1到V,那么总共为V个阶段。

        <2>状态:dp[i]表示构成面值i所需要的最少邮票数.

        <3>决策:对于样例数据1和3两种面值的邮票:

                    构成邮资0:所需要邮票张数为0张,dp[0]=0;

                    构成邮资1:只能用1分的邮票,所需要邮票张数1张,dp[1]=1;

                    构成邮资2:只能用1分的邮票,所需要邮票张数2张,dp[2]=1;

                    构成邮资3:

                             *1.若选择使用一张1分的邮票,dp[3]=dp[2]+1=3………dp[3-1]+1

                             *2.若选择使用一张3分的邮票,dp[3]=dp[0]+1=1………dp[3-3]+1

                                       dp[3]=min{dp[2]+1,dp[0]+1}=1;

                     构成邮资4:

                             *1.若选择使用一张1分的邮票,dp[4]=dp[3]+1=2………dp[3-1]+1

                             *2.若选择使用一张3分的邮票,dp[4]=dp[1]+1=1………dp[3-3]+1

                                       dp[4]=min{dp[3]+1,dp[1]+1}=2;

        <4>状态转移方程:dp[i]=min{dp[i-a[j]]+1}    i>=a[j]  1<=j<=n    f[i]<=k;

/*
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 <iostream>
#include <algorithm>

using namespace std;

int Answer;
int dp[2000001];

int main(int argc, char** argv)
{
	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 cin function.
	You may remove the comment symbols(//) in the below statement and use it.
	Use #include<cstdio> or #include <stdio.h> to use the function in your program.
	But before submission, you must remove the freopen function or rewrite comment symbols(//).
	*/

	//freopen("input.txt", "r", stdin);

	cin >> T;
	for (test_case = 0; test_case < T; test_case++)
	{

		/
		/*
		Implement your algorithm here.
		The answer to the case will be stored in variable Answer.
		*/
		/
		Answer = 0;
		int K = 0;
		int N = 0;

		cin >> K >> N;
		int *a = new int[N+1];
		for (int i = 0; i < N; i++)
		{
			cin >> a[i];
		}

		sort(a, (a + N));
		dp[0] = 0;

		while (dp[Answer] <= K)
		{
			Answer++;
			dp[Answer] = 999999;
			for (int j = 0; j < N && a[j] <= Answer; j++)
			{
				if (dp[Answer - a[j]] + 1 < dp[Answer])
					dp[Answer] = dp[Answer - a[j]] + 1;
			}
		}
		// Print the answer to standard output(screen).
		cout << "Case #" << test_case + 1 << endl;
		cout << Answer << endl;
		delete[]a;
	}

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


DESCRIPTION: 1.Analyze Problem A : sorted stamps array A={ai} ai: one stamp element in array n: array size, that is number of elements in array r: desired value of stamps F(n,r):expected result, minimum number of stamps for a given value r from n size array. S: selected stamps array S={si} 2.Choose Algorithm a.Greedy algorithm seems to be a good choice, try to solve it in O(n), i try divide array into subarry B={bi}, r should larger than every elemnt in B that is r>bi and suppose bk is the smallest element in B, so that r= bk%r, f(i,r)=(bk/r), F(n,r)=∑f(i,r). The main idea is to choose the last element who larger than desired value each time. However,it can not give us optimal solution in some condition, like A={8,5,4,1}, if r=10, this algoritm will give a solution F(n,r)=3, S={8,1,1},but the optimal solution should be F(n,r)=2, S={5,5}. b.Full search so the straight forwards algorithm is to search for every solution in A for desired value directly.However, it will always take O(n!) to go through every combination. c.Dynamic programming, at last, I decide to choose dynamic programming. analyze optimal structure, suppose in A={ai}, for a specific stamp ak,there will be two cases it is choosen so that f(i,r)=1+f(i,r-ak) , 1<=i<=k, r>=ak it is not valid so that f(i,r)=f(i-1,r) 3.Design Dynamic programming optimal structure: Compute-opt(r)= 1 + Compute-opt(r-ai) value: Compute-opt(r) = ∞ (r < 0) Compute-opt(r) = 0 (r = 0) Compute-opt(r) = 1+{Compute-opt(r-ai)} ( 1=<i<=n, r>ai>0 ) Complexity :O(nr) Memory cost:O(n+r) Compute in a bottom-up style to recursive every desired value and array. store value of Compute-opt in memory for future use, so that we can easily get value from those memory in future recursive call, and avoid compute again, that is if the array is not change, you can easily fetch result any desired value j (j < r, r is the value using for compute ). 2.For User totally, I design a small command line for this machine list below 1.Manual Operation 2.Self Auto Testing 3.Check Results q.Quit Manual Operation: when select this machine will turn to be manual mode, ask person to input stamps and desired value; Self Auto Testing:when select this machine will turn to be auto mode, show the test case already design in code after that machine will quit automatically. Check Results: only be visiable in Manual Operation, people can check desired value for the array input before, the desired value should be no more than first time input. Quit, clean all the memory and quit system.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值