动态规划:多少次可以把一个字符复制到N个字符?

如何将一个字符以最少的按键次数复制为N个字符.

算法思路:这是一个动态规划的问题,首先我们需要思考能做出的决策.[1]按键一次,输入一个字[2]全选[3]复制[4]粘贴.如果要将原来的字符变成2份.需要:[2][3][4][4],粘贴2次才能变成2倍.如果继续粘贴,我们需要k次按键将其复制为k-2倍.

现在考虑如何写出递归表达式.假设F[n]代表复制到n个字符需要的时间,那么有哪些途径可以到达F[n]的状态呢?以n=10为例进行探索.我们可以有1-〉2-〉4-〉8-〉9-〉10的途径.可以有1-〉2-〉4-〉6-〉8-〉10,还可以1-〉2-〉4-〉5-〉10,也可以1-〉2-〉3-〉6-〉9-〉1等等办法.现在我们需要考虑的是如何归纳这些途径.

我们考虑最接近F[n]的时候,如果字符小于n/2,那么总可以通过复制粘贴进入大于n/2的状态.如果等于n/2,只需4次+F[n/2]即可.如果大于n/2,这个时候继续全部复制是不可以的,会超过n的字数.排出一些明显不划算的结论,那么就有两个选择,(0):利用当前粘贴板已有的内容继续粘贴。(6->8->10)(1)引入其他操作使得状态到达n-1(8->9->10)。总之如果进入大于n/2的状态后不能通过反复粘贴达到n的话,就必须经过n-1的状态.

综上F[n] = min{F[n-1]+1,F[n/k]+k+2}//k是n的约数.

#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;

/***********定义一个Divisor类存储数1-n各自的约数*************/
class  Divisor
{
    friend int get_min_number(const Divisor&);
public:
    Divisor(size_t n) :N(n), divisor{n+1}
    {
        divisor[1] = { 1 };
        for (size_t i = 1; i != N / 2+1; ++i)
        {
            for (size_t k = 2; k*i <= N;++k)//k是倍数
            divisor[i * k].push_back(i);
        }
    }
private:
    size_t N = 0;
   vector<vector<size_t>> divisor;
};
int get_min_number(const Divisor& data)
{
    int N = data.N;
    vector<size_t> F(N+1);
    F[1] = 1;
    for (size_t i = 2; i != N + 1; ++i)
    {
        F[i] = data.N;
        for (auto k : data.divisor[i])
            F[i] = min<size_t>(F[i],min<size_t>(F[i - 1] + 1, F[i / k] + k + 2));
    }
    return F[N];
}
int main()
{
    Divisor d(100);
    auto result = get_min_number(d);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值