动态规划入门(DP)下篇

题目链接(密码hpuacm)vjudge.net/contest/244694

上篇我们对动态规划有了初步的认识,现在我们开始实战演练。

C - 数塔

在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 

有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少? 

已经告诉你了,这是个DP的题目,你能AC吗?

Input

输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。 

Output

对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。 

Sample Input

1
5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5

Sample Output

30

利用上篇中的递推公式即可。

#include <bits/stdc++.h>
using namespace std;

int arr[105][105];
int d[105][105];
int main()
{
    int c, n;
    scanf("%d", &c);
    while(c--)
    {
        scanf("%d", &n);
        memset(arr, 0, sizeof(arr));
        memset(d, 0, sizeof(d));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= i; j++)
                scanf("%d", &arr[i][j]);
        }
        for(int j = 1; j <= n; j++)
            d[n][j] = arr[n][j];
        for(int i = n - 1; i >= 1; i--)
        {
            for(int j = 1; j <= i; j++)
                d[i][j] = arr[i][j] + max(d[i+1][j], d[i+1][j+1]);
        }
        printf("%d\n", d[1][1]);
    }
    return 0;
}

D - 母牛的故事

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?

Input

输入数据由多个测试实例组成,每个测试实例占一行,包括一个整数n(0<n<55),n的含义如题目中描述。 
n=0表示输入数据的结束,不做处理。

Output

对于每个测试实例,输出在第n年的时候母牛的数量。 
每个输出占一行。

Sample Input

2
4
5
0

Sample Output

2
4
6

代码非常简短,对每年的牛的数量进行推算,可以得出第n年的牛的数量等于第n-1年的牛+第n-3年的牛,完事!

#include <bits/stdc++.h>
using namespace std;

int f(int n)
{
    if(n<=4)
        return n;
    else
        return f(n - 1) + f(n - 3);
}
int main()
{
    int n;
    while(scanf("%d", &n), n)
    {
        printf("%d\n", f(n));
    }

    return 0;
}

E - 一只小蜜蜂...

 有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。 
其中,蜂房的结构如下所示。 

Input

输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0<a<b<50)。 

Output

对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。 

Sample Input

2
1 2
3 6

Sample Output

1
3

这道题其实是一道思考题,我们需要把蜜蜂走的路径搞懂。蜜蜂爬向的蜂房只能比之前的大,比如说:从1到4,有134,124,1234这三种走法,而1324是错误的。然后我们将1-2,1-3,1-4,1-5的可能路线数列举一下(只要起点与终点差值不变,路线数也不变),发现这不就和斐波拉契数列一样吗^_^,so easy!

#include <bits/stdc++.h>
using namespace std;

long long arr[60];
int main()
{
    int n, a, b;
    scanf("%d", &n);
    arr[1] = 1;
    arr[2] = 2;
    for(int i = 3; i < 49; i++)
    {
        arr[i] = arr[i-1] + arr[i-2];
    }
    while(n--)
    {
        scanf("%d%d", &a, &b);
        printf("%lld\n", arr[b-a]);
    }
    return 0;
}

F - 超级楼梯

有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?

Input

输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1<=M<=40),表示楼梯的级数。

Output

对于每个测试实例,请输出不同走法的数量

Sample Input

2
2
3

Sample Output

1
2

同样是一道思维题,与E题类似。

#include <bits/stdc++.h>
using namespace std;

long long arr[50];
int main()
{
    int n;
    scanf("%d", &n);
    arr[1] = 0;
    arr[2] = 1;
    arr[3] = 2;
    for(int i = 4; i < 41; i++)
    {
        arr[i] = arr[i-1] + arr[i - 2];
    }
    while(n--)
    {
        int m;
        scanf("%d", &m);
        printf("%lld\n", arr[m]);
    }

    return 0;
}

G - Tickets

Jesus, what a great movie! Thousands of people are rushing to the cinema. However, this is really a tuff time for Joe who sells the film tickets. He is wandering when could he go back home as early as possible. 
A good approach, reducing the total time of tickets selling, is let adjacent people buy tickets together. As the restriction of the Ticket Seller Machine, Joe can sell a single ticket or two adjacent tickets at a time. 
Since you are the great JESUS, you know exactly how much time needed for every person to buy a single ticket or two tickets for him/her. Could you so kind to tell poor Joe at what time could he go back home as early as possible? If so, I guess Joe would full of appreciation for your help. 

Input

There are N(1<=N<=10) different scenarios, each scenario consists of 3 lines: 
1) An integer K(1<=K<=2000) representing the total number of people; 
2) K integer numbers(0s<=Si<=25s) representing the time consumed to buy a ticket for each person; 
3) (K-1) integer numbers(0s<=Di<=50s) representing the time needed for two adjacent people to buy two tickets together. 

Output

For every scenario, please tell Joe at what time could he go back home as early as possible. Every day Joe started his work at 08:00:00 am. The format of time is HH:MM:SS am|pm. 

Sample Input

2
2
20 25
40
1
8

Sample Output

08:00:40 am
08:00:08 am

动态转移方程:dp[i] = min(dp[i-1] + a[i],dp[i-2] + b[i])就是这道题的核心。

#include <bits/stdc++.h>
using namespace std;

int dp[2018];
int a[2018];
int b[2018];
int main()
{
    int n;
    scanf("%d", &n);
    while(n--)
    {
        int k;
        scanf("%d", &k);
        int h,m,s;
        memset(dp,0,sizeof(dp));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i = 1; i <= k; i++)
            scanf("%d", &a[i]);
        for(int i = 2; i <= k; i++)
            scanf("%d", &b[i]);
        dp[0] = 0;
        dp[1] = a[1];
        for(int i = 2; i <= k; i++)
            dp[i] = min(dp[i-1] + a[i],dp[i-2] + b[i]);
        h = dp[k]/3600 + 8;
        m = dp[k]/60%60;
        s = dp[k]%60;
        if(h<=12)
            printf("%02d:%02d:%02d am\n", h, m, s);
        else
            printf("%02d:%02d:%02d pm\n", h%12, m, s);
    }

    return 0;
}

H - 钱币兑换问题

在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法。请你编程序计算出共有多少种兑法。

Input

每行只有一个正整数N,N小于32768。

Output

对应每个输入,输出兑换方法数。

Sample Input

2934
12553

Sample Output

718831
13137761
#include <bits/stdc++.h>
using namespace std;

int dp[32770];
int main()
{
    int n;
    dp[0] = 1;
    for(int i = 1; i <= 3; i++)
        for(int j = i; j <= 32768; j++)
            dp[j] +=dp[j-i];

    while(scanf("%d", &n) != EOF)
    {
        printf("%d\n", dp[n]);
    }

    return 0;
}

I - 最大子段和

N个整数组成的序列a1,a2,a3,…,an,求该序列如aii+ai+1i+1+…+ajj的连续子段和的最大值。当所给的整数均为负数时和为0。

例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。

Input

第1行:整数序列的长度N(2 <= N <= 50000) 
第2 - N + 1行:N个整数(-10^9 <= Aii <= 10^9)

Output

输出最大子段和。

Sample Input

6
-2
11
-4
13
-5
-2

Sample Output

20

核心公式:sum = max(sum+arr[i],arr[i]);
                    ans  = max(ans, sum);

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

int main()
{
    int n;
    long long arr[50010];
    
    while(scanf("%d", &n) != EOF)
    {
        long long sum, ans;
        sum = 0, ans = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%lld", &arr[i]);
        }
        for(int i = 0; i < n; i++)
        {
            sum = max(sum+arr[i],arr[i]);
            ans = max(ans, sum);
        }
        printf("%lld\n", ans);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42756958/article/details/81505227