【题解】2023 DTS算法竞赛集训 第1次

比赛地址:https://www.luogu.com.cn/contest/143650

P1319 压缩技术

https://www.luogu.com.cn/problem/P1319
简单的签到模拟题

#include <iostream>//c++标准库
using namespace std;
int main(){
	int a,n,t=0,i=0,b,s=0;//t判断有没有回车,i判断输出什么,s判断有没有输完
	cin>>n;
	while(s<n*n){
		cin>>a;//循环输入a;
		i++;
		for(b=a;b>=1;b--){
			if(t==n){cout<<endl;t=0;}//判断是否需要回车,回车后t要清零
			if(i%2==1)cout<<0;
            else cout<<1;//判断是否i不被2整除,输出0,否则输出1,注意不要回车
			t++;
			s++;//t与s加一
			}
		}
	cout<<endl;
	return 0;
}

P8598 [蓝桥杯 2013 省 AB] 错误票据

https://www.luogu.com.cn/problem/P8598
这道题是判断输入的数字是否连续和重复的,那肯定是要让数字从小到大排序才能找到中断和重复数字。那排序复杂度最少是O(nlgn),是否有更快的方法?

因为输入的数字不是按照大小排序的,非常自然的想到哈希表去处理。用哈希表h记录出现的数字的次数,最后去遍历,如果出现了0次,说明中断了,如果出现了1次以上,说明重复了。

题目中给的数据范围是:正整数(不大于 1 0 5 10^5 105),因此哈希表的大小是1e5 + 5

另外要注意,如果从头遍历哈希表,前面可能有许多0,要判断更多的情况,因此可以记录下输入的最大值amax和最小值amin,在这个边界[amin,amax]里去找0和大于1的值对应的下标。

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

const int K = 1e5 + 5;
int h[K];
int main() {
    int N;
    cin >> N;
    
    int amin = 1e5;
    int amax = 0;
    int m, n; 
    int x;
    while (N--) {
        while (cin >> x) {
            if (++h[x] > 1) n = x;
            amin = min(amin, x);
            amax = max(amax, x);
        }
    }
    
    for (int i = amin; i <= amax; i++) {
        if (h[i] == 0) m = i;
    }
    cout << m << " " << n;
    return 0;
}

P1115 最大子段和

https://www.luogu.com.cn/problem/P1115
一道经典的考研及面试题,有许多解法

要求找出连续字串的最大和,那就需要确定左右区间[l,r],再计算这个区间和。

1.暴力

我们要枚举所有情况,也就是枚举出所有的区间情况,那么l取值是[0,len(s))r取值是[i,len(s)),两层for循环。确定区间后,还要遍历区间所有数字计算和,那么整体的复杂度就是O(n^3)。这个复杂度非常高。

2.前缀和

上面的暴力求解中,第三步计算区间和,我们理所当然的对应前缀和的知识点,可以用前缀和通过O(1)的时间去计算区间所有数字计算和。

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

int maxSubarraySum(vector<int>& nums) {
    int n = nums.size();
    vector<int> prefixSum(n + 1, 0); // 前缀和数组,prefixSum[i]表示前i个元素的和
    for (int i = 1; i <= n; i++) {
        prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
    }

    int maxSum = INT_MIN; // 最大和
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j <= n; j++) {
            int sum = prefixSum[j] - prefixSum[i]; // 计算从第i个元素到第j个元素的和
            maxSum = max(maxSum, sum);
        }
    }

    return maxSum;
}

int main() {
    int n;
    cin >> n;

    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }

    int maxSum = maxSubarraySum(nums);
    cout << maxSum;

    return 0;
}

但是用前缀和虽然把时间复杂度降到了O(n^2),但是依旧有的测试点过不了,我们还需要复杂度更低的代码。

在这里插入图片描述

3.贪心

考虑更低的复杂度,我们思考如何用O(n)的时间解决,也就是遍历一遍这个数组。

采用贪心的思想,记录最大和maxSum(当前为止最大的子串和)和当前和currentSum(当前为止选择的连续子串和)

遍历每个数时更新这两个变量。maxSum=max(maxSum,currentSum)这个没什么好说的。在更新currentSum时,如果 c u r r e n t S u m < 0 currentSum<0 currentSum<0,就说明从之前的起点 l l l到当前下标 i i i这段 [ l , i ] [l,i] [l,i]的和 s u m [ l , i ] < 0 sum_{[l,i]}<0 sum[l,i]<0。往后再加后面的数字 a [ i + 1 ] a[i+1] a[i+1]时,如果 l l l不变,有 s u m [ l , i ] + a [ i + 1 ] < a [ i + 1 ] sum_{[l,i]}+a[i+1]<a[i+1] sum[l,i]+a[i+1]<a[i+1],那我们肯定是要舍弃 [ l , i ] [l,i] [l,i]这一段的,从 i + 1 i+1 i+1开始重新计算,也就是令 l = i + 1 , c u r r e n t S u m = 0 l=i+1, currentSum=0 l=i+1,currentSum=0

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

int maxSubarraySum(vector<int>& nums) {
    int n = nums.size();
    int maxSum = INT_MIN; // 最大和
    int currentSum = 0; // 当前和

    for (int i = 0; i < n; i++) {
        currentSum += nums[i];

        if (currentSum > maxSum) {
            maxSum = currentSum;
        }

        if (currentSum < 0) {
            currentSum = 0;
        }
    }

    return maxSum;
}

int main() {
    int n;
    cin >> n;

    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }

    int maxSum = maxSubarraySum(nums);
    cout << maxSum;

    return 0;
}

降低复杂度之后可以通过全部的样例点。

在这里插入图片描述

4.动态规划

另一种思路是动态规划。

可以令 d p [ i ] dp[i] dp[i]表示:以 i i i结尾的连续子串最大和。

那么考虑所有的情况,结果应该是: r e s = m a x 1 ≤ i ≤ n d p [ i ] res=max_{1\leq i \leq n} dp[i] res=max1indp[i]

重点是状态转移方程。遍历到 i i i时,因为要求区间连续,只有两种情况:用 [ l , i − 1 ] [l,i-1] [l,i1]和不用 [ l , i − 1 ] [l,i-1] [l,i1]。如果用的话,那新的区间是 [ l , i ] [l,i] [l,i];如果不用,那新的区间是 [ i , i ] [i,i] [i,i]。因此有: d p [ i ] = m a x ( d p [ i − 1 ] + a [ i ] , a [ i ] ) dp[i] = max(dp[i-1]+a[i],a[i]) dp[i]=max(dp[i1]+a[i],a[i])

另外由于 d p [ i ] dp[i] dp[i]只跟 d p [ i − 1 ] dp[i-1] dp[i1]有关,dp数组可以用滚动数组优化空间。

时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( 1 ) O(1) O(1),与贪心相同。

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

int main() {
    int n;
    cin >> n;
    int ans = INT_MIN;
    int dp = 0;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        dp = max(x, dp + x);
        ans = max(ans, dp);
    }
    cout << ans;
    return 0;
}

P1002 [NOIP2002 普及组] 过河卒

https://www.luogu.com.cn/problem/P1002
首先这道题如果用搜索,每个节点两种状态,需要用dfs递归很多层。因此看看能不能用动态规划去优化重复子问题。

动态规划,每个位置只能从上面或右面走到,对应两个状态转移:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j]=dp[i-1][j]+dp[i][j-1] dp[i][j]=dp[i1][j]+dp[i][j1]
另外要注意,马对应上面的位置下标有可能越界,为了方便起见,我们将所有的坐标对应的+2

#include<bits/stdc++.h> 

using namespace std;
long long int dp[40][40], ma[40][40];
int n, m, a, b;
int main() {
	cin >> n >> m >> a >> b;
	n += 2, m += 2, a += 2, b += 2;
	ma[a][b] = 1;
	ma[a - 1][b + 2] = 1;
	ma[a - 1][b - 2] = 1;
	ma[a + 1][b - 2] = 1;
	ma[a + 1][b + 2] = 1;
	ma[a + 2][b - 1] = 1;
	ma[a + 2][b + 1] = 1;
	ma[a - 2][b - 1] = 1;
	ma[a - 2][b + 1] = 1;
	dp[1][2] = 1;
	for (int i = 2; i <= n; i++) {
		for (int j = 2; j <= m; j++) {
			if (ma[i][j] == 1) {
				continue;
			}
			else dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
		}
	}
	cout << dp[n][m];
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《算法艺术与信息学竞赛题解pdf》是一本介绍算法艺术和信息学竞赛题解的教材。它通过详细解析一系列典型的竞赛题目,讲解了如何使用不同的算法数据结构来解决这些问题。 在这本教材中,作者首先介绍了算法和信息学竞赛的基本知识,包括常用的数据结构算法思想。然后,他通过具体的例子和题目,展示了如何应用这些知识来解决实际问题。每个题目都有详细的解析过程,包括问题的分析、算法的设计和优化等内容。 这本教材的特点之一是注重实践。作者通过大量的实例和练习题,帮助读者巩固所学的知识,并掌握解决问题的方法。此外,他还提供了一些常见的竞赛技巧和经验,帮助读者在竞赛中取得好的成绩。 《算法艺术与信息学竞赛题解pdf》适合对算法和信息学竞赛感兴趣的读者。无论是初学者还是有一定基础的读者,都可以从中受益。通过学习这本教材,读者不仅可以提高解决问题的能力,还可以培养逻辑思维和计算机编程的能力。 总之,这本教材提供了一种全面的学习算法和信息学竞赛的方式。通过深入浅出的讲解和丰富的实例,它帮助读者建立起坚实的算法基础,提高解决问题的能力,并在竞赛中取得优异的成绩。 ### 回答2: 算法艺术与信息学竞赛题解pdf是一本以算法和信息学竞赛题为内容的电子书,提供了有关这些题目的详细解答。该书的出版旨在帮助读者更好地理解和掌握算法和信息学竞赛的核心知识和技巧。 首先,这本书介绍了一些常见的算法数据结构,如贪心算法动态规划、图论等。通过逐一解析题目,并给出相应的算法设计和实现思路,读者可以学习到不同类型题目的解题方法和技巧。 其,该书还强调了对问题进行建模的重要性。在解决问题时,合理的问题建模可以将问题转化为更易于理解和求解的形式。书中通过具体的例子,教给读者如何抽象问题,构建合适的数据结构来解决实际问题。 此外,该书还提供了大量典型题目的详细解答,包括解题思路、具体实现和代码示例等。读者可以通过参考这些题目的解答,了解不同类型题目的解题思路,提高自己的解题能力。 总之,算法艺术与信息学竞赛题解pdf是一本帮助读者提高算法和信息学竞赛能力的实用电子书。通过学习其中的知识和技巧,读者可以更好地解决相关问题,并在竞赛中获得优异成绩。 ### 回答3: 《算法艺术与信息学竞赛题解PDF》是一本内容丰富的电子书,主要讲解了算法艺术和信息学竞赛中常见的题目解法。该书以清晰简洁的语言,详细介绍了解题思路和具体实现过程。 这本电子书中涵盖了多个题型,包括排列组合、图论、动态规划、贪心算法等。通过这些经典的题目,读者可以了解到不同算法在解决问题时的特点和应用场景,提升算法设计和编程能力。 该电子书特色之一是讲解了信息学竞赛中被广泛使用的算法数据结构,如并查集、最短路径算法、网络流等。阅读该书可以让读者对这些常用的算法有更深入的理解,从而在解决实际问题时能够选择适当的算法。 此外,该电子书为了方便读者理解,还提供了大量的实例,以演示不同算法的具体应用。这些实例不仅帮助读者掌握算法的思维方式,还能够培养读者的问题分析和解决能力。 总之,《算法艺术与信息学竞赛题解PDF》是一本非常实用的电子书,适合对算法和信息学竞赛感兴趣的读者。通过阅读该书,读者可以提高解题速度和准确度,增强算法设计和编程能力,对解决问题的思路和方法有更深入的认识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值