01背包
裸背包问题,相关资料参见 dd 大神的总结:《背包问题九讲》
按照常规的背包思路,构建 10001 * 101 的二维数组 f[i][j],状态 f[i][j] 表示前 i 枚硬币能拼凑出的小于等于 j 的最大值(j 这里代表一个价格)。
状态转移方程为:f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + c[i]), 其中 c[i]为第 i 枚硬币的面值,c[i]为排序过的硬币面值数组。
由于最终的输出要求是排序的币值的字典序最小的组合,可以对 c[i]做从大到小的排序,并另开一个数组 has[i][j] 来记录当前状态下,是否有包含 c[i]。同时注意,当f[i - 1][j] == f[i - 1][j - c[i]] + c[i]时,采纳当前的 c[i],以满足字典序。
**has同时记录了满足条件的多条路径,通过weight值从小到大查找路径,从而得到满足字典序的结果
#include<stdio.h>
#include<vector>
#include<algorithm>
#define SIZE 10001
#define INFINTE -0x7fffffff
using namespace std;
int f[SIZE][101],weight[SIZE];
bool has[SIZE][101];
vector<int> p[101],v;
bool cmp(const int &a, const int &b){
return a > b;
}
bool FindCoin(int n, int m){
int i,j;
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
f[i][j] = 0;
for (i = 1; i <= n; i++){
for (j = weight[i]; j <= m; j++){
if (f[i - 1][j] > f[i - 1][j - weight[i]] + weight[i])
f[i][j] = f[i - 1][j];
else{
f[i][j] = f[i - 1][j - weight[i]] + weight[i];
has[i][j] = true;
}
}
}
if (f[n][m] == m)
return true;
else return false;
}
int main(){
freopen("1.in", "r", stdin);
int n,m;
scanf("%d%d", &n,&m);
int i;
for (i = 1; i <= n; i++)
scanf("%d", &weight[i]);
sort(weight + 1, weight + n + 1,cmp);
bool flag = FindCoin(n,m);
if (flag){
while (m){
while (!has[n][m])
n--;
v.push_back(weight[n]);
m -= weight[n];
n--;
}
for (i = 0; i < v.size(); i++){
printf("%d", v[i]);
if (i == v.size() - 1)
putchar('\n');
else putchar(' ');
}
}
else printf("No Solution\n");
return 0;
}