AcWing第50场周赛

题目列表

AcWing 4416. 缺少的数

题目描述

给定一个长度为 n−1 的数列 a1,a2,…,an−1。

数列中的元素两两不同,且都在 1∼n 的范围内。

请你计算,1∼n 中的哪一个数没有在数列中出现过。

输入格式
第一行包含一个整数 n。

第二行包含 n−1 个整数 a1,a2,…,an−1。

输出格式
输出 1∼n 中没有在数列中出现过的数。

数据范围
前三个测试点满足 2≤n≤10。
所有测试点满足 2≤n≤105,1≤ai≤n。

输入样例:
10
3 8 10 1 7 9 6 5 2
输出样例:
4

分析

n - 1个不重复的数都在1~n之间,求缺失的数,可以利用等差数列求和公式减去所有数的和就是缺失的数,单个数据范围是10w级别,和可能会爆int,需要用long long存储。
也可以使用hash解决,遍历数组将每个出现过的元素置为true,最后遍历一下1~n,如果哪个元素在hash表里面没有出现或者值是false,就是缺失的元素,这里采用hash表解决。

代码

#include <cstdio>
using namespace std;
const int N = 100005;
bool m[N];
int main(){
    int n,x;
    scanf("%d",&n);
    for(int i = 1;i < n;i++) {
        scanf("%d",&x);
        m[x] = true;
    }
    for(int i = 1;i <= n;i++) {
        if(!m[i]) {
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}

AcWing 4417. 选区间

题目描述

给定 n 个一类区间 (l1,i,r1,i)。

给定 m 个二类区间 (l2,i,r2,i)。

请你从一类区间中挑选一个区间,从二类区间中挑选一个区间。

要求,选出的两个区间之间的距离尽可能大。

请你输出最大可能距离。

关于两区间 (l1,r1) 和 (l2,r2) 之间的距离,我们规定:

如果两区间存在交集,则区间距离为 0。
如果两区间不存在交集,则区间距离为 |i−j| 的最小可能值,其中 l1≤i≤r1,l2≤j≤r2。
输入格式
第一行包含一个整数 n。

接下来 n 行,每行包含两个整数 l1,i,r1,i。

再一行包含一个整数 m。

最后 m 行,每行包含两个整数 l2,i,r2,i。

输出格式
一个整数,表示最大可能距离。

数据范围
前三个测试点满足 1≤n,m≤10。
所有测试点满足 1≤n,m≤2×105,1≤l1,i≤r1,i≤109,1≤l2,i≤r2,i≤109

输入样例1:
3
1 5
2 6
2 3
2
2 4
6 8
输出样例1:
3
输入样例2:
3
1 5
2 6
3 7
2
2 4
1 4
输出样例2:
0

分析

本题求两组区间的区间距离的最大值,比如[1,2]和[4,5]的区间距离就是4-2=2。对于同一组区间而言,区间距离的最大值必然是在拥有最小的右端点的区间和拥有最大的左端点的区间之间,这样一来一个区间的左端点减去另一个区间的右端点的距离才最大,也就是被减数取最大,减数取最小,差就最大。如果只有一组区间,还需要考虑我们求得的最小右端点和最大左端点是不是同属同一个区间,这样一来求得的区间距离就不准确了,但是本题给了两组区间,显然简化了逻辑,我们只需要考虑每组区间的最小右端点和最大左端点即可。
设第一组区间的最大左端点是l1,最小右端点是r1;第二组区间的最大左端点是l2,最小右端点是r2,则两组区间的区间距离的最大值为max(l1 - r2, l2 - r1),当然,区间距离不能小于0,所以最终结果如果是负数,区间距离就是0。

代码

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 200005;
int a[N],b[N];
int main(){
    int n,m;
    scanf("%d",&n);
    int l,r;
    int l1 = 0,r1=1e9;
    for(int i = 0;i < n;i++) {
        scanf("%d%d",&l,&r);
        if(l > l1)  l1 = l;
        if(r < r1)  r1 = r;
    }
    scanf("%d",&m);
    int l2 = 0,r2=1e9;
    for(int i = 0;i < m;i++) {
        scanf("%d%d",&l,&r);
        if(l > l2)  l2 = l;
        if(r < r2)  r2 = r;
    }
    int res = max(l1 - r2,l2 - r1);
    printf("%d\n",max(res,0));
    return 0;
}

AcWing 4418. 选元素

题目描述

给定一个长度为 n 的整数序列 a1,a2,…,an。

请你从中挑选 x 个元素,要求:

原序列中的每一个长度为 k 的连续子序列都至少包含一个被选中的元素。
满足条件 1 的前提下,所选 x 个元素的相加之和应尽可能大。
输出最大可能和。

输入格式
第一行包含三个整数 n,k,x。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
如果无法满足题目要求,则输出 −1。

否则,输出一个整数,表示所选元素的最大可能和。

数据范围
前三个测试点满足 1≤k,x≤n≤6。
所有测试点满足 1≤k,x≤n≤200,1≤ai≤109

输入样例1:
5 2 3
5 1 3 10 1
输出样例1:
18
输入样例2:
6 1 5
10 30 30 70 10 10
输出样例2:
-1
输入样例3:
4 3 1
1 100 1 1
输出样例3:
100

分析

本题要在n个元素里选x个元素,使得这x个元素的和最大,并且n个元素里每个长度为m (题目里是k,这里改为m方便后续区别)的连续子序列都至少包含一个被选中的元素。这种求最优解的问题很明显可以用DP解决,DP求解首先要考虑的是状态表示,如果用f[i][j]表示前i个元素里选j个元素的且每个长度为m的连续子序列都包含被选元素的元素最大和,为了表述方便,后面的描述就省去每个长度为m的连续子序列都包含被选元素这句话了,也就是用f[i][j]表示前i个元素里选j个元素的最大和,那么本题求的就是f[n][x],这种状态表示比较简洁,能够直接求出答案,但是不方便进行状态转移,因为条件限制了任意相邻两个被选元素之间的距离不能超过m,为此,我们需要知道每种状态下最后一个被选元素是谁,比如求f[i][j],我们需要知道前i - 1个元素选择的最后一个元素是哪个,如果与第i个元素的距离超过了m,就不能转移过来了,因为此时不论第i个元素选不选择都不符合条件。
求前i个元素里选j个元素的最大和有两种思路。
一种是枚举这j个元素最后一个元素出现的位置,可以令f[i][j][k]表示前i个元素里选择j个元素且最后一个选择的元素位于k的最大和,这种在状态表示里加上一维表示限制条件的方法比较常见,那么最终的答案就在f[n][x][k]里搜索即可,枚举一下最后一个元素出现的位置k,就可以求出最大和。这种方法表示简单,容易想到,但是计算量大而且状态转移时需要考虑的限制也很多。
另一个思路也是枚举最后一个元素出现的位置,不过不增加状态表示的维度,用f[i][j]表示前i个元素里选择j个元素且最后一个元素位于第i个位置的最大和,相当于把之前的三维表示的第一维和第三维合并了,这样一来第i个元素是必选的,所以状态划分时更加的简洁。而最终的答案就是f[k][x],枚举一下最后一个元素出现的位置k,求出其中的最大和即可。
由于第二种思路更加高效,所以先介绍第二种思路,状态表示:f[i][j]表示前i个元素里选择j个元素且最后一个元素位于第i个位置的最大和,既然第i个元素必选,那么我们只需要枚举第j - 1个选择的元素出现的位置就可以了。状态转移方程为f[i][j] = max(f[t][j-1] + a[i]),唯一需要考虑的就是t的范围,既然选第j - 1个元素,t肯定不能小于j - 1,而且两个被选中元素之间距离不能超过m,所以i - t <= m,t >= i - m,另外,作为下标,t也不能为负数。还有点最不能忽略的就是状态的边界,也就是初始状态的初始化,求方案时经常初始化什么都不选的方案数是1,这里f[0][0]表示什么都不选的最大和,自然是0,并且后续所有的状态都由该状态逐步转移而来,其他状态必须设置为不可达,一般设置为-INF,不会影响求和的最大值。比如f[0][j]一旦j不为0,从0个元素里选择j个元素肯定是不合法的,如果不初始化后续将转移到其他的状态,比如不初始化f[0][1]就是0,选择了第1个元素状态就变成了f[1][2],而且这个状态有了合法的值,这显然是不合理的。
周赛时我使用的是三维状态表示,用f[i][j][k]表示前i个元素里选择j个元素且最后一个元素位于k的最大和,但是当时忽略了状态边界条件,导致没有及时调出来,所以前面才特别强调平时容易忽略的DP的状态初始化。三维状态的初始状态为f[i][0][0]=0,i从0到n,也就是前i个元素里选择0个元素的最大和是0,其他状态都是不合法的-INF。再来考虑这种状态表示下的状态划分和状态转移。这时候就需要考虑第i个元素选与不选了,如果不选,是否能直接继承f[i-1][j][k]的值呢?答案是否定的,因为一旦第i个元素到最后一个被选择的元素k之间的距离不小于m,这种状态就是不合法的了,并不能直接继承前面的状态,所以需要加上限制i - k < m,才能继承上一轮的状态。如果选择第i个元素,就和之前方法的状态转移是一样的了,f[i][j][k] = max(f[i-1][j-1][t]),t的范围也是不小于j-1和不小于i - m以及不小于0。

代码

二维状态表示解法

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205;
typedef long long ll;
int a[N];
ll f[N][N];
int main(){
    int n,m,x;
    cin>>n>>m>>x;
    for(int i = 1;i <= n;i++)   cin>>a[i];
    memset(f,-0x3f,sizeof f);
    f[0][0] = 0;
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= x && j <= i;j++){
            int t = max(0, i - m);
            for(;t < i;t++) f[i][j] = max(f[i][j],f[t][j-1] + a[i]);
        }
    }
    ll res = -1;
    //这里i <= n - m的状态不能参与答案统计,因为会引起连续m个元素都没选。
    for(int i = n - m + 1;i <= n;i++)   res = max(res,f[i][x]);
    cout<<res<<endl;
    return 0;
}

三维状态表示解法

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205;
typedef long long ll;
int a[N];
ll f[N][N][N];
int main(){
    int n,m,x;
    cin>>n>>m>>x;
    for(int i = 1;i <= n;i++)   cin>>a[i];
    memset(f,-0x3f,sizeof f);
    for(int i = 0;i <= n;i++)   f[i][0][0] = 0;
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= x && j <= i;j++){
            for(int k = j;k <= i;k++){
                if(i - k < m)    f[i][j][k] = f[i-1][j][k];
            }
            int t = max(j - 1,i - m);
            for(;t < i;t++)    f[i][j][i] = max(f[i][j][i],f[i-1][j-1][t] + a[i]);
        }
    }
    ll res = -1;
    int i = max(x,n - m + 1);
    for(;i <= n;i++)   res = max(res, f[n][x][i]);
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值