入门算法题——动态规划篇(一)

转载请注明出处:http://www.cnblogs.com/xianyue

题1:教师的困惑

题目描述

高考结束了,高三(一)班的王老师又即将要送走他的这一届的学生。为了给学生们留个念想,她决定去商店里为每一个学生买一个礼物。王老师班里一共有n个学生,商店里一共有m件礼物,每一件礼物都有各自的价格。王老师需要从这m件礼物里面购买n件,但是她买之前发现一个问题:如果给A同学买的礼物比B同学贵,B同学很可能就会觉得不公平。为了尽量避免这种情况的出现,王老师希望购买的n件礼物中最贵的哪一件的价格和最便宜的那一件的价格之差最小。请你帮王老师计算一下从m件礼物中购买n件使得最贵礼物和最便宜礼物地价格差最小对应地最小的价格差。

输入

第一行两个正整数n和m(2<=n<=m<=50)。
第二行m个正整数表示m件礼物的价格。

输出

输出从m件礼物中购买n件使得最贵礼物和最便宜礼物地价格差最小对应地最小的价格差。

样例输入

4 6
10 12 10 7 5 22

样例输出

5

题解

这道题我们需要先将m个数排序,然后从n-1到m-1遍历i,答案是a[i]-a[i-n+1]中地最小值。

代码

#include <iostream>
#include <algorithm>
using namespace std;
#define inf (1<<29)
int n, m, f = inf, a[55];
int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++)
        cin >> a[i];
    sort(a, a+m);
    for (int i = n-1; i < m; i ++)
        f = min(f, a[i]-a[i-n+1]);
    cout << f << endl;
    return 0;
}

题2:取数字1

题意

给你一个长度为n地数组a,你每次可以从其中取一个数a[k],当你取完a[k]之后,a[k-1]和a[k+1](如果有的话)便被锁定了(也就是说你取了a[k]之后便永远不能取a[k-1]和a[k+1]了),同时你将会获得a[k]地积分。求你最多能获得的积分。

输入

第一行一个数n。
接下来一行n个正整数表示数组a。

输出

你所能获得的最大的积分。

样例输入

5
5 3 5 3 4

样例输出

14

题解

这道题可以列状态转移方程:

f[i] = a[i] + max(f[i-2], f[i-3])

其中f[i]表示a[0]到a[i]这i+1个数并且我取了a[i]所获得的最大积分。

代码

#include <iostream>
using namespace std;
const int maxn = 100010;
int n, a[maxn], f[maxn], res = 0;
int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++)
        cin >> a[i];
    f[0] = a[0];
    f[1] = a[1];
    f[2] = a[0] + a[2];
    for (int i = 3; i < n; i ++)
    {
        f[i] = a[i] + max(f[i-2], f[i-3]);
    }
    for (int i = 0; i < n; i ++)
        res = max(res , f[i]);
    cout << res << endl;
    return 0;
}

题3:取数字2

题意

给你一个长度为n地数组a,你每次可以从其中取一个数a[k],当你取完a[k]之后,数组中所有值为a[k]-1或a[k]+1的数都会被删除,同时你将会获得a[k]地积分。求你最多能获得的积分。

输入

第一行一个数n。(1<=n<=100000)
接下来一行n个正整数表示数组a。(1<=a[i]<=100000)

输出

你所能获得的最大的积分。

样例输入

5
5 3 5 3 4

样例输出

16

题解

可以列出状态转移方程:

f[i] = max(f[i-2], f[i-3]) + i * num[i];

其中,num[i]为a[i]==i的数的个数。
f[i]表示数值从0到i这个范围,且选择了所有数值为i的数,所能得到的总积分。

代码

#include <iostream>
#include <map>
using namespace std;
map<int, int> num;
const int maxn = 100010;
int n, a[maxn];
long long f[maxn];
int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++)
    {
        cin >> a[i];
        num[ a[i] ] ++;
    }
    f[0] = 0;
    f[1] = 1 * num[1];
    f[2] = 2 * num[2];
    for (int i = 2; i <= 100000+3; i ++)
    {
        f[i] = max(f[i-2], f[i-3]) + (long long)i * (long long)num[i];
    }
    cout << f[100000+3] << endl;
    return 0;
}

题4:减丝带

题目描述

小丽和小梅剪丝带。
已知四代的长度为n米,要求剪出来的四代的长度必须是a或b或c米。求:在满足条件的情况下它们最多能够剪出多少条四代。

输入

一行四个整数n,a,b,c。

输出

最多能够剪出的丝带的数目。

样例输入

7 5 5 2

样例输出

2

题解

这道题有点类似完全背包问题:
只不过背包被要求要正好装满。
所以一开始做了处理:除了f[0]以外的f[i]都置为一个无穷小的数。(关于f的计算见代码)

代码

#include <iostream>
using namespace std;
const int maxn = 4010;
#define inf (1<<29)
int n, a, f[maxn];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++)
        f[i] = -inf;
    for (int i = 0; i < 3; i ++)
    {
        cin >> a;
        for (int j = a; j <= n; j ++)
            f[j] = max(f[j], f[j-a]+1);
    }
    cout << f[n] << endl;
    return 0;
}

题5:翻转游戏

题目大意

有一个长度为n的数组a,a[i]为0或者1。你可以选择两个整数i和j(0<=i<=j<n),然后对数组a的从a[i]到a[j]的数进行一次翻转操作(即a[i]到a[j]的每一个数x都会变为1-x),求进行了一次反转操作后,数组a中n个数的和最大为多少?

输入

第一行包括一个数n。
第二行n个数表示数组a。

输出

进行一次翻转操作后最大的可能总和。

样例输入

5
1 0 0 1 0

样例输出

4

题解

根据数组a我们可以求得一个数组b。
b[i]表示a[i]翻转后的获利。

  • 如果a[i]一开始为0,则翻转后变为1,获利1
  • 如果a[i]一开始为1,则翻转后变为0,获利-1

据此,我们可以对数组b求一个最大连续子序列和,将该最大连续子序列和加上数组a的元素总和便为答案。

代码

#include <iostream>
using namespace std;
const int maxn = 110;
int a[maxn], b[maxn], n, sum = 0, t;
int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++)
    {
        cin >> a[i];
        b[i] = (a[i] ? -1 : 1);
        sum += a[i];
    }
    t = b[0];
    int c = t;
    for (int i = 1; i < n; i ++)
    {
        c = max(b[i], c + b[i]);
        if (c > t)
            t = c;
    }
    cout << sum + t << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/xianyue/p/7137099.html