1068 Find More Coins

1068 Find More Coins

题目大意

用n个硬币购买价值为m的东西,输出使用方案,使得几个硬币加起来价值为m。从小到大排序,输出最小的那个方案。

基本思路

0-1背包问题的变形,这里的容量和价值都用面额表示。
数据结构:
定义数组w,w[i]为第i个硬币的面额。读入后需要逆序排列,为什么请看注释说明。
定义数组dp,阶段数隐含(每个硬币的选择问题表示一个阶段),dp[j]表示在阶段i(阶段i从1枚举到n)面额限制为j(面额j从m逆序枚举到w[i])的情况下所能获得的最大面额。
定义数组choice,choice[i][j]表示在第i个阶段面额限制为j的情况下是否选取的第i个硬币,选取了这个硬币则choice[i][j]为1。
基本思路:
读入每个硬币面额后逆序排列。
进行0-1背包问题的求解。
根据dp[m]的不同取值做出不同的输出。

代码

具体数据结构和详细思路请看代码注释:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n,m;
int w[10010];
int dp[10010];//滚动数组:阶段数隐含,dp[i]表示在某个阶段下面额限制为i的情况下所能获得的最大面额(这个0-1背包问题中容积和价值一致,都用硬币面额表示)
int choice[10010][10010];//choice[i][j]表示在第i个阶段下面额限制为j的情况下是否选取的第i个硬币,选取了则choice[i][j]为1(每个硬币的选择问题表示一个阶段)
bool cmp1(int a,int b){
    return a>b;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    //因为在输出那一步中,需要逆序遍历每个阶段(获得那个阶段那个硬币的选取情况)并且 升序输出选取的硬币面额,所以需要将数组w降序排序
	sort(w + 1, w + n + 1, cmp1);
	//进行0-1背包问题的求解
	for (int i = 1; i <= n;i++){//枚举每个阶段
		for (int j = m; j >= w[i];j--){//逆序枚举面额限制
			//如果在第i个阶段面额限制为j的情况下:选取了第i个硬币所能获得的最大面额比不选取所能获得的面额大,则更新数组dp[j]和choice[i][j]
			if(dp[j]<=dp[j-w[i]]+w[i]){
				dp[j] = dp[j - w[i]] + w[i];
				choice[i][j] = 1;
			}
		}
	}
	//分情况输出
	if(dp[m]!=m){
		cout << "No Solution" << endl;
	}else{
		//逆序枚举每个阶段,获得每个阶段对应的那个硬币的选取情况,阶段大于0时循环:
		//如果没有选取那个硬币,则index--
		//如果选取了那个硬币,则把这个硬币存入数组arr,面额限制减去这个硬币的面额(v=v-w[i]),index--
		vector<int> arr;
		int index = n, v = m;//阶段,面额限制
		while(index>0){
			if(choice[index][v]==1){
				arr.push_back(w[index]);
				v = v - w[index];
			}
			index--;
		}
		for (int i = 0; i < arr.size();i++){
			if(i==0){
				cout << arr[i];
			}else{
				cout << " " << arr[i];
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值