ACM 3月29日总结

一. dp动态规划

动态规划是分阶段求最优值的算法。

将复杂问题按阶段划分成子问题;

枚举子问题各种可能情况,从中找出最优值;

利用子问题的最优值求得源问题的最优解

1动态规划的基本模型

动态规划问题具有以下基本特征:

1 问题具有多阶段决策的特征。

2 每一阶段都有相应的“状态”与之对应,描述状态的量称为“状态变量”。

3 每一阶段都面临一个决策,选择不同的决策将会导致下一阶段不同的状态。

4 每一阶段的最优解问题可以递归地归结为下一阶段各个可能状态的最优解问题,各子问题与原问题具有完全相同的结构。

2基本概念

阶段:据空间顺序或时间顺序对问题的求解划分阶段。

状态:描述事物的性质,不同事物有不同的性质,因而用不同的状态来刻画。对问题的求解状态的描述是分阶段的。

决策:根据题意要求,对每个阶段所做出的某种选择性操作。

状态转移方程:用数学公式描述与阶段相关的状态间的演变规律。

3解题的一般步骤

1、判断问题是否具有最优子结构性质,若不具备则不能用动态规划。

2、把问题分成若干个子问题(分阶段)。

3、建立状态转移方程(递推公式)。

4、找出边界条件。

5、将已知边界值带入方程。

6、递推求解。

二. vjudge题目

题目1

Description

Given a set of n integers: A={a1, a2,…,an}, we define a function d(A) as below:
在这里插入图片描述
Your task is to calculate d(A).

Input

The input consists of T(<=30) test
cases. The number of test cases (T) is given in the first line of the input.
Each test case contains two lines. The
first line is an integer n(2<=n<=50000). The second line contains n
integers: a1, a2, …, an. (|ai| <= 10000).There is an empty line after each
case.

Output

Print exactly one line for each test case.
The line should contain the integer d(A).

Sample Input

1
10
1 -1 2 2 3 -3 4 -4 5 -5

Sample Output

13

Hint

In the sample, we choose {2,2,3,-3,4} and{5}, then we can get the answer.
Huge input,scanf is recommended

解题过程:
这个题是最大二子段和问题
这个题才开始做的时候,选了一个用枚举的办法选一个中间值 i,用求最大子段和的方法求出右边的最大子段和,用同样的方法再求出i左边的最长子段和,两边的最长子段和加起来,就是所要求的最大二子段和问题,这个代码思路没错,我也测试了好多数据,但是却超时了

超时错误代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<cstdio>
using namespace std;
#define N 10005
int a[N],dp[N],sum,maxx=0,Max=0;
int main()
{
    int k;
    cin>>k;
    int n;
    while(k--)
    {cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];

        for(int i=1;i<=n;i++)
    {dp[1]=max(0,a[1]);
        for(int j=2;j<=i;j++)

             dp[j]=max(dp[j-1]+a[j],a[j]);
         for(int j=1;j<=i;j++)
                maxx=max(maxx,dp[i]);
                dp[i+1]=max(0,a[i+1]);
         for(int k=i+2;k<=n;k++)
             dp[k]=max(dp[k-1]+a[k],a[k]);
             for(int j=i+1;j<=n;j++)
                 Max=max(maxx+dp[j],Max);

    }
    printf("%d",Max);
    }
}

因为如果数据很长的话,对于每一个i,都要计算一次右边“最长子段和”和左边的最长子段和,如此,会有很多重复计算,然后我就想能不能把这些重复的计算简化,一下子都算出来,于是就想到了分别用数组来储存整个序列到i处"最大左子段和left[n]“和"右子段和right[n]”,这样只用计算一次就把计算,就把所有的数据计算出了,因此,以后每次计算都不用再算了,直接从数组里取就行了,有点类似于记忆化搜索

AC代码

#include<cstdio>
#include<algorithm>
using namespace std;

#define N 50005
const int INF = 0x3ffffff;
int a[N],left[N],right[N];
int main()
{
    int k;
    scanf("%d",&k);
    while(k--)
    {
        int n;
        scanf("%d",&n);

        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        left[1]=a[1];
        for(int i=2;i<=n;i++)
        left[i]=max(a[i]+left[i-1],a[i]);

        for(int i=2;i<=n;i++)
            left[i]=max(left[i-1],left[i]);
        right[n]=a[n];
        for(int i=n-1;i>=1;i--)
            right[i]=max(right[i+1]+a[i],a[i]);
        int maxx=-INF;
        for(int i=1;i<n;i++)
            maxx=max(maxx,left[i]+right[i+1]);
            printf("%d\n",maxx);

    }
}

题目2

description

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

解题过程:
这个题特别像之前的贪心安排节目的问题,不同的是,贪心让求最多可选择的区间数目,但这个题,让求的却不是数目,而是选择不同区间可得到的最大的收益,也就是说,区间数量多并不代表着收益就一定大,解决这个问题只能用dp,首先一定要排序,我是按照区间后端从大到小排的序,然后对于每一个区间就只有选与不选两个决定,如果选这个区间可能就代表着与这个区间重合的区间没有办法选,因此我定义了一个数组pre[i],记录与第i个区间不重叠但最接近的区间是哪个所以就得出了状态转移方程 dp[i]=max(dp[i-1],dp[pre[i]]+a[i].v);dp[i-1]代表不选第i个区间,而dp[pre[i]]+a[i].v代表选第i个区间,下面的就好办了

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

struct val
{
    int l,r,v;
};
bool cmp(val a,val b)
{
   return a.r<b.r;
}


const int N=1005;
int pre[N],dp[N];
val a[N];
int main()
{
    int N,M,R;

    cin>>N>>M>>R;
    for(int i=1;i<=M;i++)
      {cin>>a[i].l>>a[i].r>>a[i].v;
      a[i].r+=R;
      }

      sort(a+1,a+1+M,cmp);
      for(int i=M;i>1;i--)
      {bool p=true;
          for(int j=i-1;j>=1;j--)
          {
              if(a[j].r<=a[i].l)
              {pre[i]=j;
              p=false;
              break;
              }

          }
          if(p)pre[i]=0;
      }dp[0]=0;pre[1]=0;
      for(int i=1;i<=M;i++)
      {
          dp[i]=max(dp[i-1],dp[pre[i]]+a[i].v);
      }

      cout<<dp[M];
}

题目三

description

Farmer John’s cows would like to jump over the moon, just like the cows in their favorite nursery rhyme. Unfortunately, cows can not jump.
The local witch doctor has mixed up P (1 <= P <= 150,000) potions to aid the cows in their quest to jump. These potions must be administered exactly in the order they were created, though some may be skipped.
Each potion has a ‘strength’ (1 <= strength <= 500) that enhances the cows’ jumping ability. Taking a potion during an odd time step increases the cows’ jump; taking a potion during an even time step decreases the jump. Before taking any potions the cows’ jumping ability is, of course, 0.
No potion can be taken twice, and once the cow has begun taking potions, one potion must be taken during each time step, starting at time 1. One or more potions may be skipped in each turn.
Determine which potions to take to get the highest jump.

Input

Line 1: A single integer, P
Lines 2…P+1: Each line contains a single integer that is the strength of a potion. Line 2 gives the strength of the first potion; line 3 gives the strength of the second potion; and so on.
Output
Line 1: A single integer that is the maximum possible jump.

Sample Input

8
7
2
1
8
4
3
5
6

Sample Output

17

解题过程:
看到这个题半天没有思路,就写了数据分析,准备找规律,找了半天规律是找到了,结果不是dp,是贪心
思路如下:
把原序列分割成若干个小段,保证每个小段最大值在最小值之前,而每次吃药的时候就先吃最大值,再吃最小值,就能保证弹跳力在不断的增加。

然后就怎么也想不起来用dp怎么做。。。。

就上网看了看别人怎么做的;
因为我想的是用dp[i]这样的数组来表示到 i 时可以得到的最大值,头都想破了就是想不出来,跟前面做过的题不一样,想了半天就是不知道怎么处理第 i 个数到底是加还是减,唉。看了别人的思路后豁然开朗,因为对第 i 个数的处理方式只有两种,如果把它当做第奇数个数那就是加,把它当做第偶数个数处理就是减,因此只有两个过程,可以用一个二维数组 来表示状态

动态规划:

dp[i][0] 表示当拿到第i个药时,之前已经拿了奇数个药的最大弹跳力。

dp[i][1] 表示
当拿到第i个药时,之前已经拿了偶数个药的最大弹跳力。

方程就是,

dp[i][0] =max(dp[i-1][0], dp[i-1][1]- v[i]);

dp[i][1] =max(dp[i-1][1], dp[i-1][0]+v[i]);

一定要记住老师说过的话:**对于动态规划来说,数组的下标和存储的数据都是代表着一定的意义的
**
对于这个题有两个指标,一个是第i个数,另一个是如何对这个数进行处理,用二维数组就使问题简单多了

代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[151000][2];
int maxd(int a,int b)
{
    return a<b?b:a;
}
int main()
{
    int n,i,t;
    while (scanf("%d",&n) != EOF)
    {
        dp[0][0]=0;
        dp[0][1]=0;
        for (i=1; i<=n; i++)
        {
            scanf("%d",&t);
            dp[i][0]=maxd(dp[i-1][0],dp[i-1][1]-t);
            dp[i][1]=maxd(dp[i-1][1],dp[i-1][0]+t);
        }
        printf("%d\n",maxd(dp[n][0],dp[n][1]));
    }
}
感悟:

一定不能着急做题,要仔细读题分析,先把题理解透彻,遇到实在不会的题一定要将其搞懂,把题的思路彻底分析明白,与前面做过的题进行对比,找出相同和不同,做好归纳和总结,只有不断总结,归纳和积累才能不断提升。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值