用vector来记录凑某个面值的最小序列。
与平时做的动态规划转移方式稍稍有点区别,但是思想是一样的。
代码的注释已经比较清楚了,这里就不过多赘述想法了。
#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
using namespace std;
const int N = 1e2 + 5;
bool operator<(vector<int> &a, vector<int> &b) //重载小于比较符
{
for (int i = 0; i < min(a.size(), b.size()); i++)
{
if (a[i] < b[i]) return 1;
if (a[i] > b[i]) return 0;
}
if (a.size() < b.size()) return 1;
return 0;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
vector<int> dp[N]; // dp[i]表示凑出i的最小硬币序列
int coin[N], n, m, c;
mem(coin, 0);
cin >> n >> m;
for (int i = 0; i < n; i++) //统计各面值硬币数量
{
cin >> c;
if (c > m) continue; //大于m的硬币没必要统计
coin[c]++;
}
dp[0].push_back(0); //为了方便计算在dp[0]中加0,后续输出时要去掉
for (int i = 1; i <= m; i++) //从小到大枚举硬币面值
{
if (coin[i] == 0) continue;
for (int j = 1; j <= min(coin[i], m / i); j++) //优化1:对于面值为i的硬币,最多枚举m/i次就够了
{
for (int k = m; k >= i; k--) //枚举总面值
{
if (dp[k - i].size() > 0) // k - i的面值凑出来过
{
if (dp[k].size() == 0) //如果此面值之前还凑不出来,直接在dp[k - i]的序列后加i
{
dp[k] = dp[k - i];
dp[k].push_back(i);
}
else //如果凑出来过,判断两种序列的大小
{
vector<int> tmp = dp[k - i];
tmp.push_back(i);
if (tmp < dp[k]) dp[k] = tmp;
}
}
}
}
}
if (dp[m].size() != 0)
{
for (int i = 0; i < dp[m].size(); i++)
{
if (dp[m][i] == 0) continue; // 0不用输出
cout << dp[m][i];
if (i != dp[m].size() - 1) cout << " ";
}
cout << endl;
}
else
cout << "No Solution" << endl;
return 0;
}