2022-02-11每日刷题打卡

140 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

2022-02-11每日刷题打卡

AcWing——算法基础

9. 分组背包问题 - AcWing题库

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

  • 每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
  • 每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例:

8

01背包的扩展,在这一题,我们也是只能选拿或不拿,如果拿了只能拿一个,那么我们就要判断在这一组里拿哪一个物品可以使得我们的价值总和最高。和01背包的解比起来,我们多了一个循环来遍历同一组的所有物品,然后一个个计算拿了这个物品后能得到的最大价值,其余写法和原理和01背包一样。

#include<iostream>
using namespace std;

const int N=110;
int f[N],v[N],w[N];

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int s;
        cin>>s;
        for(int j=0;j<s;j++)cin>>v[j]>>w[j];
        for(int j=m;j>=0;j--)
        {
            for(int k=0;k<s;k++)
            {
                if(j>=v[k])f[j]=max(f[j],f[j-v[k]]+w[k]);
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

898. 数字三角形 - AcWing题库

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

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

输入格式

第一行包含整数 n,表示数字三角形的层数。

接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。

输出格式

输出一个整数,表示最大的路径数字和。

数据范围

1≤n≤500
−10000≤三角形中的整数≤10000

输入样例:

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

输出样例:

30

看到这题一开始的想法是模拟,从上往下走,每次对比左下和右下位置的数,选大的那一方,但很快就发现不对劲,就拿样例来说,如果用这个方法,最后得出的结果是28,并不是30,所以这个想法不对。而且如果我们从上往下走的话,每次要分开两条路,这样才能完全模拟出所有的结果并从中取出最大的,意思就是,如果有n层,那我们要模拟2^n条路径,n最大能取500,所以模拟的思路是不对的。

但每次选最大的总是没错,不如我们从下往上走。每次两两数做对比,较大的那个往上走一位,并且把它加到那个位置上的数,比如样例来说,4和5我们就取5加到他们头上的数2里,这样2就变成7,然后继续最后一层的两两比较,比完后就往上一层继续两两比较,直到顶部,得到的就是我们要的最大值。

#include<iostream>
using namespace std;

const int N=505;
int arr[N][N],f[N][N];

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<=i;j++)
            cin>>arr[i][j];
    for(int i=0;i<n;i++)f[n-1][i]=arr[n-1][i];
    for(int i=n-2;i>=0;i--)
    {
        for(int j=0;j<=i;j++)
        {
            f[i][j]=max(f[i+1][j],f[i+1][j+1])+arr[i][j];
        }
    }
    cout<<f[0][0]<<endl;
    
    return 0;
}

蓝桥杯——算法提高

算法提高 最大连续子段和

问题描述

给出一个长为n的数列,a1,a2,……,an,求和最大的连续子序列,即找到一对(i,j),i<=j,使ai+ai+1+……+aj的和最大,输出这个和

输入格式

第一行为正整数n

第二行n个用空格分开的整数

表示a1,a2,……,an

输出格式

一个整数,表示最大连续子序列的和

样例输入

3

-1 -2 -3

样例输出

-1

数据规模和约定

1<=n<=105,-105<=ai<=10^5

题目说的是子段和,题目描述说的又是子序列和。。。淦哦这俩玩意差很大的好嘛。

不过写完后发现也确实是字段和,准备一个数组w,接收数据后开始遍历,从第2个位置开始遍历,每次遍历比较一下前面的数是负数还是正数,如果是正数就把它加到自己身上来,同时维护一下最大值res,如果是负数,加到自己身上也会使得自身的数减小,所以不加。就是这么个过程,结束后输出最大值res即可。

(顺便说说子序列和子段,子段是不能断开的,比如数组-1 、2、 -1 、4、-5,如果要算的是子段和,那最多是 2+(-1)+4=5,但如果是子序列就不一样了,子序列可以跳着选,只要顺序不变就行,所以最多可以是2+4=6。子段就是数组的一段,是固定不变的,子序列是只要原来数的相对顺序不发生改变咋选都行。这题如果是子序列的话,就遍历数组,把所有的正数加起来就行,如果一个正数都没有就找一个最大的负数也行。)

#include<iostream>
using namespace std;

const int N = 100010;
int w[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> w[i];
    }
    int res = w[0];
    for (int i = 1; i < n; i++)
    {
        if (w[i - 1] > 0)
        {
            w[i] += w[i - 1];       
        }
        res = max(res, w[i]);
    }
    cout << res << endl;

    return 0;
}