线性动态规划(1)


  又是一周,还没开学。网课有网课的优点,但上网课的缺点已经在我这发挥的淋漓尽致了。不过,敲代码好像成了一种特殊的乐趣,混不自觉地从其它科目榨取着时间,啧啧,开学越晚,要补的越多。所以,快开学吧。
  言归正传,动态规划,先来看看这个名字。与贪心相比,贪心的名字更注重于描绘这种算法的神,而动态规划则直截了当地刻画它的形。因此,但从名字就能看出来,这一算法解决的一类问题的特点就是动态,动态的过程,动态的数据,某一次或者某一个量的改变会带动其它某量或状态的改变。而你对此算法的应用,就是一种规划,将动态的东西按照对于解题有益的顺序进行。而线性动态规划,就是你可以找到一条路径,将多变的数据沿着这一路径展开,逐步求解。
  再来看看一般解题步骤。首先,对于动态规划问题有着几个概念,即

在这里插入图片描述
  阶段对应状态,状态与决策结合得出状态转移方程。由此,可以看出,在做题时,有两点需要着重考虑。第一点,就是阶段,如何划分阶段,或者说以什么划分阶段。这其实就是动态规划尤其线性动态规划中那条解题的路径,沿此路径决定阶段,寻找状态。第二点,就是决策,如何做出选择,某一状态做出怎样选择。这其实就是如何规划,选择怎样的准则进行规划,将这一路径进行下去。至于状态转移方程,有了决策,状态转移方程也是呼之欲出,在心中即可。
  回头再看,动态规划是将一个复杂问题分解成子问题,求得子问题的最优解,利用子问题的最优解回溯出原问题的解。这种思想看起来和贪心挺类似,而具体操作又感觉有点递推的影子。不过,动态规划与贪心最大的区别就是在于子问题的求解上,贪心是在子问题中选出局部最优解,所以解决问题的重心放在某一子问题最优解的选取上,而动态规划则是找出状态变化时子问题最优解的变化,所以重心放在子问题最优解的选取之间关系上,这是本质区别。就好像这里有一堆答案,其中有正确答案,也有错误答案,贪心就是制定了一种方案,以从这些答案中一下子选出正确的答案,而动态规划则在考虑上次怎样选取的正确答案,而这次选取和上次有什么关系,至于这次具体怎么选,一个一个瞧瞧就得了。而至于递推,我感觉他们就像长大了的双胞胎姐妹花,同根而生而生又各有风采,能够解决同样或者类似问题但又有所不同。具体哪不同,不晓得,但给人一种动态规划更加高阶一样,好像。
  啰里啰嗦的最终还是要做题啊,按照先前所说步骤做题,会很容易切入题目的核心,就拿个简单题为例吧(难题也没做到啊,不过即使做到了,可能还不会做呢)。
  看题:
  Bessie is such a hard-working cow. In fact, she is so focused on maximizing her productivity that she decides to schedule her next N (1 ≤ N ≤ 1,000,000) hours (conveniently labeled 0…N-1) so that she produces as much milk as possible.

Farmer John has a list of M (1 ≤ M ≤ 1,000) possibly overlapping intervals in which he is available for milking. Each interval i has a starting hour (0 ≤ starting_houri ≤ N), an ending hour (starting_houri < ending_houri ≤ N), and a corresponding efficiency (1 ≤ efficiencyi ≤ 1,000,000) which indicates how many gallons of milk that he can get out of Bessie in that interval. Farmer John starts and stops milking at the beginning of the starting hour and ending hour, respectively. When being milked, Bessie must be milked through an entire interval.

Even Bessie has her limitations, though. After being milked during any interval, she must rest R (1 ≤ R ≤ N) hours before she can start milking again. Given Farmer Johns list of intervals, determine the maximum amount of milk that Bessie can produce in the N hours.

Input

  • Line 1: Three space-separated integers: N, M, and R
  • Lines 2…M+1: Line i+1 describes FJ’s ith milking interval withthree space-separated integers: starting_houri , ending_houri , and efficiencyi

Output

  • Line 1: The maximum number of gallons of milk that Bessie can product in the N hours

Sample Input
12 4 2
1 2 8
10 12 19
3 6 24
7 10 31
Sample Output
43
  算是自己做的第一个线性规划题目了吧,英文的。
  看到题目,首先划分阶段。此题能够用来划分阶段,最显眼的是时间,按照时间进行,每一阶段寻求某一时间得到的最优解,应该可以,但这一方法我却放弃了,因为时间范围很大,因为每次工作时间跨度很大,中间会进行多次没有意义的计算,搞不好超时的话会很麻烦。因此,还有一种可以用来划分阶段的便是这些计划的工作,以计划的工作在时间线上的顺序划分阶段,可以省略去那些不必要的计算,同样还可以简化状态变换的关系,使问题的求解得到简化。因此,将各计划的起始和结束时间建立在结构体中,对其进行排序,以起始时间为排序标准。
  然后找寻各状态间的关系。寻找状态间的关系,研究的对象便是某一中间的状态或者最终结尾的状态(起始状态就是个最简单的边界量嘛)。继而考虑,当到了这一次工作的时间时,看它前一个最近的一个工作的结束时间,如果已经过去了休息的时间间隔就可以在前一次产量基础上再加上这次工作的产量了,如果不是呢,就加上在之前的一次的产量就可以了(即使上次工作结束时间与此次工作开始时间紧挨着,那么上次工作时间最短也是两个小时,因为起始时间与结束时间是不相等的,因此前次的结束时间最早也在休息时间开外,所以不冲突,可以直接加上)。由此关系(状态转移方程)一直到最末次的工作,取这其间能够得到的最大值即可。
  带入了几组数据都可以,就提交了,然后,wrong answer!
  为什么呢?忽然想到,为什么时间不可以重叠呢,此前我以为,既然是奶牛自己制定的计划,它没必要给自己制定一个矛盾的计划表吧。可能,这只是个题目,人家不管奶牛怎样,人家只认得数据。所以在做题时,不要将题目的场景过多的带入,题目中出现的任何事物,都要把他们考虑成只能制造数据的机器,不用管是牛还人...无情!
  因此在时间段排序时改为以开始时间为准,开始时间相同时在以结束时间排序。在研究当前状态时,要枚举先前的所有状态,找到符合休息条件且能得到最的大产量,提交,通过了。
  不过我还是不放心,于是把题目全文翻译了一下。好嘛,这不是奶牛的计划表,而是挤奶那哥们的计划表,他可能有事某个时间段不确定倒也可能。自认为英文的题目能够看懂,可想要做到和看汉语一样,还得努力学英语啊。话说回来,再不开学我的英语就要彻底荒废了。
  下面是AC的代码:

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
struct section{
    int x,y;
    long long ef,sum;
};
bool cmp(section a,section b){
    if(a.x==b.x) return a.y<b.y;
    else return a.x<b.x;
}
int main()
{
    int t,n,rt;
    long long maxx=-1;
    section a[1010]={};
    cin>>t>>n>>rt;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].x>>a[i].y>>a[i].ef;
        a[i].sum=a[i].ef;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        if(i==1) a[1].sum=a[1].ef;
        else
        for(int j=1;j<i;j++)
        if(a[i].x-a[j].y>=rt) a[i].sum=max(a[i].sum,a[j].sum+a[i].ef);
        maxx=max(maxx,a[i].sum);
    }
    cout<<maxx<<endl;
}

这个题目告诉我,读题很重要,读英语题目很重要,and,学英语很重要。
  同时,通过做这部分题,感受到了老师曾经提到过的一个问题。当刻意为锻炼这一算法做题时,会有意识的向动态规划的一般模式去靠拢,对于这一算法的运用自然提高了,但却不愿去琢磨其它解题的方法了,因为当你第一时间想到这种算法时i,就有种“它是最好的”感觉,而不想再去考虑其它的方法了。记得做递归部分题目时,好多题还不走递归函数,或者找寻特殊方法解出来,现在很少了。
  当然,现阶段的算法的练习还是必要的,至于以后会怎样,谁知道呢!毕竟,知识的增长也不会是坏事。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值