嘘~ 正在从服务器偷取页面 . . .

模拟赛题讲解[14]


模拟赛题讲解[14]

来自 yukuai26 2022-08-06 noi.ac #2755

题目背景

$\text{yukuai26}$ 喜欢算术,但他又菜又爱玩,所以需要你的帮助

题目描述

小明给你 $n$ 个正整数,他想取一些数加起来(可以取 $0$ 个数),问能得到的最大的能被 $3$ 整除的和是多少

输入格式

第一行一个整数 $n$

第二行 $n$ 个正整数, 表示小明给你的 $n$ 个正整数

输出格式

一个整数,表示最大的能被 $3$ 整除的和

输入1

4
1 2 3 4

输出1

9

输入2

3
693 647 550

输出2

1890

数据范围

总共 $10$ 个测试点

对于测试点 $1,2$,$n \leq 3$

对于测试点 $3$,$n \leq 10$

对于测试点 $4,5$,保证小明给你的数只有 $3$ 的倍数

对于测试点 $6$,保证小明给的数对 $3$取模为 $0$ 或 $1$

对于测试点 $7$,$a_i \leq 100$

对于所有测试点 $n \leq 100000 ,a_i \leq 1000$

题解

解法一 贪心

先把所有的数都加上

那么最后的和模 $3$ 只有 $3$ 种情况

  • $S\bmod 3=0$ 直接输出答案即可
  • $S\bmod 3=1$ 去掉一个最小的模 $3$ 余 $1$ 的数即可
  • $S\bmod 3=2$ 去掉「两个最小的模 $3$ 余 $1$ 的数」和「一个最小的模 $3$ 余 $1$ 的数」中的较小者即可

时间复杂度 $O(n\log n)$

代码没写,因为考场上dp过了

解法二 DP

对于每个数,我们可以看作重量为 $w_i = x \bmod 3,v_i=x$ 的物品

设 $f_{i,j}$ 表示只考虑前 $i$ 个物品,总和模 $3$ 为 $j$ 时的最大总和,则

然后就是简单的01背包啦

时间复杂度 $O(n)$

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e5+15)

int n,o,res,w[N],v[N],f[N][3];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i=1,x; i<=n; i++)
    {
        cin >> x;
        if(x%3==0) res+=x;
        else ++o,w[o]=x%3,v[o]=x;
    }
    memset(f,0xc0,sizeof(f));
    f[0][0]=0;
    for(int i=0; i<o; i++)
        for(int j=0; j<=2; j++)
            f[i+1][(j+w[i+1])%3]=max(f[i][(j+w[i+1])%3],f[i][j]+v[i+1]);
    cout << res+f[o][0] << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录