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];
}
}
}
}