题目描述
突然停电了, 新新需要点蜡烛, 当新新翻箱倒柜找到火柴时, 又来电了.
新新很苦恼, 这时新新打算玩一个小游戏缓解一下.
新新有 n 根火柴, 火柴可以拼凑出上述的数字, 但是只可以拼 m 种数字, 请问必须用完所有火柴, 能得到的最大数字是多少?
输入格式
第一行两个整数 n,m.
第二行 m 个整数 ai, 表示只能够拼凑哪种数字.
输出格式
输出一个整数, 表示所能得到的最大数字.
思路:题目要求我们使用一定数量的火柴棒来组合出最大的数字,而每个数字都有其规定的火柴棒数量,因而问题可以被转化为背包问题,每个数字就是一个物品,要求使用所有火柴棒恰好拼出最大价值数字。
又因为题目没有规定每个数字最多选1次,所以这是一个完全装满多重背包问题.
先定义了一个结构体Node来表示每个物品,即每个数字。其中w表示该数所需的火柴棒数,v表示可表示的数字。也就是重量和价值.
struct Node {
long long w; // 需要的火柴棒数量
char v; // 所能代表的数字的字符值
};
又因为最大数字要让大的数字在前,所以进行排序.
bool cmp(Node x, Node y) {
return x.v > y.v; // 反向排序,由大到小更优
}
sort(a+1, a+c+1, cmp); // 根据数值从大到小排序
接下来就按照普通的完全装满多重背包问题做即可,只不过价值均用的是字符串表示.
下面是进行空间优化后的代码
#include <bits/stdc++.h>
using namespace std;
struct Node {
long long w; // 需要的火柴棒数量
char v; // 所能代表的数字的字符值
};
long long m, c;
Node a[20];
long long b[20] = {0, 2, 5, 5, 4, 5, 6, 3, 7, 6};
string f[10010]; // 最大价值
bool cmp(Node x, Node y) {
return x.v > y.v; // 反向排序,由大到小更优
}
bool fun(string a, string b) { // 如果两个字符串长度一样,则比较字典序,否则比较长度
if(a.size() == b.size())
return a < b; // 比较字典序
else
return a.size() < b.size(); // 比较长度
}
signed main() {
cin >> m >> c;
for(long long i=1; i<=c; i++) {
long long x;
cin >> x;
a[i].w = b[x]; // 需要消耗的数量
a[i].v = x + '0'; // 所能提供的价值
}
sort(a+1, a+c+1, cmp); // 根据数值从大到小排序
f[0] = "";
for(long long i=1; i<=c; i++) { // 每种物品
for(long long j=a[i].w; j<=m; j++) { // 找到所有大于等于它的子问题,进行状态转移方程计算
if(j - a[i].w == 0 || f[j - a[i].w] != "") {
string tmp = f[j-a[i].w] + a[i].v;
if(fun(f[j], tmp)) {
f[j] = tmp;
}
}
}
}
cout << f[m]; // 输出最终的答案
return 0;
}
ps:题解中的n变量貌似没啥用