凑零钱-dynamic programming

凑零钱 (30 分)
韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有 10​(4)(下同没改哈哈哈)枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。

题目其他格式太不对CSDN的编辑器了,其他读者自己找一下,或许你们已经不用看了hh

注:我们说序列{ A[1],A[2],⋯ }比{ B[1],B[2],⋯ }“小”,是指存在 k≥1 使得 A[i]=B[i] 对所有 i<k 成立,并且 A[k]<B[k]。

输入样例 1:
8 9
5 9 8 7 2 3 4 1
输出样例 1:
1 3 5
输入样例 2:
4 8
7 2 4 3
输出样例 2:
No Solution

然后呢,这道题目肯定是动态规划这个重要的方法论的知识了,动态规划确实是需要静下心来慢慢琢磨的一个方面。因为要节省时间,就要使用更多的空间,这是我在这道题目中体会到的。

然后这道题目源码给出(不是dl勿喷hh )

然后这道题目就是体现出了动态规划的最重要的流程,第一步要想好怎么建立状态转移方程,子问题怎么才是重叠可以有相互关系的。

这道题目我的 最后思想是:从–给出的序列中的最小值–到--要凑足的总钱款之间建立规划,从小到大依次计算凑够每个值需要的序列,后一个的序列由前面计算过的序列产生。

这样一个动态规划的总计划。当然在实行过程中,要注意一些诸如插入排序之类的东西。

注意输出不要多余空格。


//动态规划--7-13 凑零钱问题
//从--给出的序列中的最小值--到--要凑足的总钱款之间建立规划,从小到大依次计算凑够每个值需要的序列,后一个的序列由前面计算过的序列产生


#include <iostream>
#include<algorithm>
#include<vector>
#include<string.h>
#include<string>

using namespace std;



int n, m; 
int price[10010];//记录依次输入的序列值,然后在序列中重新排序

vector<int> have;//存入给定序列的序列值,(记录下每个值的个数),自始至终不变

vector<int> list[10010];//记录凑够每个值所需要的最小序列,用于从小到大算的状态转移过程

vector<int> cal[10010];//记录产生每个值对应的最小序列后,当前序列的剩余序列值

vector<int> temp;//暂存可能交换的序列,用于比较

int start;//动态规划的开始值,是给出的序列的最小值



//比较,产生较小的序列
bool cmp(vector<int> a, vector<int> b)
{
	int lena = a.size(); int lenb = b.size();
	int lenc = lena < lenb ? lena : lenb;

	for (int i = 0; i < lenc; i++)
	{
		if (a[i] < b[i])
		{
			return true;

		}
		else if (a[i] > b[i])
		{
			return false;
		}
		else
		{

			continue;
		}
	}
	return false;
}

//动态规划
void toDo()
{
	//给第一个结点赋初值
	start = price[0];
	list[0].push_back(start);
	cal[0][start]--;

	for (int i = start +1; i <= m; i++)
	{
		cal[i - start] = have; int done = 0;
		for (int j = i-1; j >=start; j--)
		{
			if (cal[j-start][i - j] > 0&& list[j - start].size()!=0)
			{
				if (!done) 
				{
					cal[i - start] = cal[j - start];
					cal[i - start][i - j]--;

					list[i - start] = list[j - start];
					vector<int>::iterator it = lower_bound(list[i - start].begin(), list[i - start].end(), i - j);
					list[i - start].insert(it,i-j);

					done = 1; 
				}
				else
				{
					//比较得出较小的序列
					temp = list[j - start];
					vector<int>::iterator it = lower_bound(temp.begin(), temp.end(), i - j);
					temp.insert(it, i - j);

					if (cmp(temp, list[i - start]) == true)
					{
						list[i - start] = temp;
						cal[i - start] = cal[j - start];
						cal[i - start][i - j]--;
					}

				}
			}
		}
		if (!done)
		{
			//没有找到符合要求的则要进行操作,给其赋一个合适的序列
			list[i - start].clear();
			if (have[i])
			{
				list[i - start].push_back(i);
				cal[i - start][i]--;

			}
		}
		else
		{
			continue;
		}

	}
}
int main()
{
	cin >> n >> m; cal[0].resize(10010);
	have.resize(10010);
	for (int i = 0; i < n; i++)
	{
		cin >> price[i];
		cal[0][price[i]]++;
		have[price[i]]++;
	}

	sort(price, price + n);

	toDo();

	//输出
	if (list[m - start].size() == 0)
	{
		cout << "No Solution";
	}
	else
	{
		//末尾不出现空格
		for (int i = 0; i < list[m - start].size()-1; i++)
		{
			cout << list[m - start][i] << " ";
		}
		cout << list[m - start][list[m - start].size() - 1];

	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值