1361: 最大盈利——dp问题

由于本人水平较低,尚未接触最优算法,因此在做这题的时候自以为非常NB的用了暴力解法,结果就是一直Time Limit Exceed,然后没有办法的我只能去网上搜博客寻找思路,结果刚好找到一篇,原来这道题目是需要用dynamic programming(简称dp),即动态规划。

忽然回想起来,其实这学期也零零散散地接触了一些动态规划的问题,这个思想确实有点小难,不是很容易弄懂,我目前也只是处于门外汉的境界,目前看到过的dp问题有:背包问题,最短路径,关键路径。

其实这一类问题都是大同小异,关键是把握dp问题的核心思想,也就是如何抽象过程,将一个大过程分成一个个小过程,在小过程中达到最优效果,然后不断迭代,使得大过程也变成最优。

下面是遇到的和dp问题相关的一套题目

1361: 最大盈利

Time Limit: 1 Sec Memory Limit: 64 MB
Submit: 3406 Solved: 896

Description

徐老师前一段时间也在股市里玩了一把,而她是很细心的人,将每一天的盈亏的情况都按顺序记在本子上了,分别是a[1],a[2],…a[n]. 现在你的任务是帮她计算一下,从哪一天到哪一天这段时间里她的盈利是最多的。比如,(6,-1,5,4,-7), 她盈利最多的是6 + (-1) + 5 + 4 = 14.

Input

第一行输入一个整数T, 表示她有T本账本(最多不超过20),对于每一本账本,都有一行数据。每行数据第一个数字是一个整数N ( 1 <= N <= 100000 ), 然后是N个整数(-500到500之间)。

Output

对于每本账本,你要输出2行,第一行是:“Payoff #:”, #是账本号. 第2行包含三个数,最大盈利、该最大盈利起始编号与终止编号。如果有很多这样的结果,只输出第一个出现的最大盈利的情况。在2个账本之间要输出一个空行。

Sample Input

2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7 -5

Sample Output

Payoff 1:
14 1 4

Payoff 2:
7 1 6

暴力解法代码如下:

#include<stdio.h>
#include<math.h>
int main()
{
    int n;
    scanf("%d",&n);
    int p=1;
    while(n--)
    {
        int x;
        scanf("%d",&x);
        int i;
        int a[x+1];
        for(i=1;i<=x;i++)
        {
            scanf("%d",&a[i]);
        }
        int begin=1,end=1,j;
        int max=a[1];
        for(i=1;i<=x;i++)
        {
            int sum=0;
            if(a[i]>=0)
            {
            for(j=i;j<=x;j++)
            {
                 sum=sum+a[j];
                 if(sum>max)
                 {
                    max=sum;
                    begin=i;
                    end=j;
                 }
            }
            }
        }
        printf("Payoff %d:\n",p) ;
        printf("%d %d %d\n",max,begin,end);
        p++;
        putchar('\n');
    }
}

思路非常简单,就是穷尽所有情况,然后找出最大值(注释我就不写了。)
但是当数据非常多的时候,这个方法是非常慢的,效率很低。

很多人都说程序员和码农是俩个生物,确实是这样,其实学习到了后期,实现功能这一块都是比较基础的,主要是看你编写的程序效率高不高,运行快不快,稳不稳定。而码农就是直接莽,暴力解下去,自以为写了很多代码,感觉自己很牛逼,其实麻烦的要死。(对,就是我。。)而程序员就是很会“偷懒”,用最简洁的代码,最佳的算法来编写程序。

好了,说了一些题外话。

下面是优化后的算法

#include<stdio.h>
#include<math.h>
int main()
{
    int n;
    scanf("%d",&n);
    int p=1;
    while(n--)
    {
        int x;
        scanf("%d",&x);
        int i;
        int a[x+1];
        for(i=1;i<=x;i++)
        {
            scanf("%d",&a[i]);
        }
        int begin1=1,begin2=1,end=1,j;
        int max=a[1],sum=a[1];
        for(i=2;i<=x;i++)		
        {
            if(sum+a[i]>=a[i])	//加上目标
            {
                sum=sum+a[i];
            }
            else	//放弃前面,加上目标
            {
               sum=a[i];
               begin2=i;
            }
            if(sum>max)		//选择每次确定的最大值中的最大值
            {
                max=sum;
                end=i;
                begin1=begin2;
            }

        }
        printf("Payoff %d:\n",p) ;
        printf("%d %d %d\n",max,begin1,end);
        p++;
        putchar('\n');
    }
}

这里面最难理解的地方就是这句“选择每次确定的最大值中的最大值”,因为你每次确定一个最大值,实际上是局部的最大值,而你要的是全局的最大值,因此才会在每次确定一个局部最大值sum后,再和全局最大值max进行比较,让max等于更大的那个值。

如果觉得有帮助,可以关注一下我的公众号,我的公众号主要是将这些文章进行美化加工,以更加精美的方式展现出来,同时记录我大学四年的生活,谢谢你们!
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值