火柴游戏题解

文章描述了一种利用火柴棒组合最大数字的问题,将问题转化为完全装满多重背包问题来解决。通过定义结构体表示数字,然后对数字按值降序排序,再用动态规划的状态转移方程求解。最后给出C++代码实现来找出能用所有火柴拼出的最大数字。
摘要由CSDN通过智能技术生成

题目描述

突然停电了, 新新需要点蜡烛, 当新新翻箱倒柜找到火柴时, 又来电了.

新新很苦恼, 这时新新打算玩一个小游戏缓解一下.

新新有 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变量貌似没啥用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值