20230724

复赛

简单的求和

做题原因

之前TLE30和TLE60,一直没有做出来

做题过程

  1. 想到了尺取法,重新听了尺取法的课,看了尺取法的模板。
  2. 看了之前错误的代码,决定沿用之前使用map的方法,但是在找不出之前的错误,重新写了一遍代码
  3. 交上去AC

解题思路

  • 首先输入的元素是不需要按照它本来的顺序的(可以重新排序)

  • 需要给元素进行排序(set或者map,但是set会去重,所以选择map)

  • 因为是计算加法,所以只要计算过一次,所有相同的元素都使用(map)

  • 需要记录元素的个数(map)
    根据以上三点,我选用了map

  • 相加刚好等于k,所以比k大或者比k小都需要调整范围

  • 元素是排好序的
    根据以上两点,我选择了尺取法

代码实现

auto left = mp.begin();//设置左指针,map的第一个元素  
auto right = mp.end(); right--;//设置右指针,map的最后一个元素  
while (left != right) {//当左指针和右指针不相等时(相等考虑的情况不一样)  
if (left->first + right->first == k) {//如果相加等于k  
ans += left->second * right->second;//做指针的元素任意取出一个和右指针的元素任意取出一个进行搭配都可以,所以时乘法  
left++;//将左指针后移  
if (left == right) {//如果相等,循环结束,和上面的判断条件一样  
break;  
}  
right--;//因为这两个数相加刚好等于k,所以任何一个数加减都不会再等于k,所以这里面右指针也要向前移动  
} else if (left->first + right->first > k) {//如果大于  
right--;//那么右指针向前移动,减少其中一个数  
} else if (left->first + right->first < k) {//如果小于  
left++;//那么左指针向后移动,增大第一个数  
}  
}  
if (2 * left->first == k) {//如果相等,那么要考虑的就是这个数乘2是否等于k  
ans += left->second * (left->second - 1) / 2;//从这个数里面取出任意一个,这个数的数量减少1,然后再从这些数里面取出一个,但为了避免同样的a和b,第一次取出a然后取出b,第二次取出b然后取出a的情况,需要除以2  
}

长度最小的子数组

做题原因

练习尺取法

做题思路

因为数据范围为\(1\le n\le 3\times 10^{5}\)
所以每个都遍历的话会就是\(O\left ( n^{2} \right )\)
肯定会TLE,
所以想到了尺取法。

尺取的具体方式:如果未达到要求,那么右移右指针扩大范围;如果已经达到要求,那么右移左指针缩小范围。

代码实现

正确代码

        long long sum = 0;//两个指针之前的总和
        int aMin = 1e9;//最短的距离
        for (int i = 0, j = 0; i < n; i++) {//左指针不能到达右边界
            while (sum < s && j < n) {//如果没有大于等于,那么右移右指针,扩大范围
                sum += a[j];//加上
                j += 1;//右指针指向范围内最后一个的后一个
            }
            if (sum >= s) {
                aMin = min(aMin, j - i);
            }
            sum -= a[i];
        }
        cout << (aMin == 1e9 ? 0 : aMin) << "\n";

错误代码

        long long ans = INT_MAX;
        for (long long i = 0; i < n; i++) {
            long long sum = 0;
            for (long long j = i; j < n; j++) {
                // 设置中止条件
                sum += a[j];
                if (sum >= s) {
                    long long tmLen = j - i + 1;
                    ans = min(ans, tmLen);
                    break;
                }
            }

错误原因:TLE,时间复杂度\(O\left ( n^{2} \right )\)

激光炸弹

做题思路

因为要使范围内目标价值总和最大,
所以要使用前缀和。

因为有x,y两个方向,
所以是二维前缀和。

矩阵翻转

矩阵的每个元素只会是0或1,
当反转次数为偶数的时候,元素不变;当反转次数为奇数时,元素取反
所以要记录每个元素反转的奇偶性
用差分

初赛

T1

#include <iostream>
using namespace std;
void fun(char *a, char *b, int &c)
{
    a = b;
    (*a)++;
    c++;
}
int main()
{
    char c1, c2, *p1, *p2;
    c1 = 'A';
    c2 = 'a';
    int c3;
    c3 = 48;
    p1 = &c1;
    p2 = &c2;
    fun(p1, p2, c3);
    cout << c1 << " " << c2 << " " << c3 << endl;
    return 0;
}

第3行和第16行的‘&’含义相同。

错误。因为第三行的‘&’表示引用,第16 行的‘&’表示取字符变量c1的地址。

程序输出结果为:

A b 49
a=p1,b=p2,a=b=p2, (*a)++相当于(*p2)++,相当于c2++,因此c1不变,为‘A’,c2变成’b',&c=c3,是引用,c就是c3,同时变,因此c=49。

T2

#include<bits/stdc++.h>
using namespace std;
int a[100005],b[100005],c[100005];
int main() {
	int n,i,j,m,k=0,sign=0;
	cin>>n;
	for(i=0; i<n; ++i) {
		cin>>a[i];
		c[i]=a[i];
	}
	sort(c,c+n);
	for(j=0; j<n-1; ++j) {
		if(a[j]==c[n-1]) {
			k=n-1-j;
		}
		if(a[j+1]<a[j])
			sign=1;
	}
	if(sign==0) {
		cout<<0<<endl;
		return 0;
	}
	for(i=0; i<n; ++i)
		b[(i+k)%n]=a[i];
	for(i=0; i<n; ++i) {
		if(b[i]!=c[i]) {
			cout<<-1<<endl;
			return 0;
		}
	}
	cout<<k<<endl;
	return 0;
 }

11行c数组排序下标范围为\(\left ( 0,n- 1 \right )\)

错误,\(\left ( 0,n- 1 \right )\)是开区间,而排序为闭区间\(\left [ 0,n-1 \right ]\)

算法时间复杂度为\(O\left ( n\log{n} \right )\)

对的,sort的时间复杂度是\(O\left ( n\log{n} \right )\),其他都是\(O\left ( n \right )\)

T3

#include < bits/stdc++.h >
using namespace std;
const int maxn = 500000, INF = 0x3f3f3f3f;
int L[maxn / 2 + 2], R[maxn / 2 + 2];
void unknown(int a[], int n, int left, int mid, int right)
{
    int n1 = mid - left, n2 = right - mid;
    for (int i = 0; i < n1; i++)
        L[i] = a[left + i];
    for (int i = 0; i < n2; i++)
        R[i] = a[mid + i];
    L[n1] = R[n2] = INF;
    int i = 0, j = 0;
    for (int k = left; k < right; k++)
    {
        if (L[i] < = R[j])
            a[k] = L[i++];
        else
            a[k] = R[j++];
    }
}
void unknownsort(int a[], int n, int left, int right)
{
    if (left + 1 < right)
    {
        int mid = (left + right) / 2;
        unknownsort(a, n, left, mid);
        unknownsort(a, n, mid, right);
        unknown(a, n, left, mid, right);
    }
}
int main()
{
    int a[maxn], n;
    cin >> n;
    for (int i = 0; i < n; i++)
    cin >> a[i];
    unknownsort(a, n, 0, n);
    for (int i = 0; i < n; i++)
    {
        if (i)
            cout < < "  ";
        cout < < a[i];
    }
    cout < < endl;
    return 0;
}

第21行的<改为<=将不会改变运行结果

错误

此类排序方法是高效的但是不稳定

错误,归并排序是一种稳定的排序

第4行的2个“+2”都去掉将不会改变运行结果。

错误

T5

```plaintext
#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cstring>  
using namespace std;  
const int maxn = 200;  
const int dx[]={0,0,1,-1};  
const int dy[]={1,-1,0,0};  
int r,c,ans;  
int map[maxn][maxn],step[maxn][maxn];  
int dfs(int x,int y){  
    if(①) return step[x][y];  
    step[x][y]=②;  
    for(int i=0;i<4;i++){   
        int nx=x+dx[i],ny=y+dy[i];  
        if(③){   
            step[x][y]=max(step[x][y],1+dfs(nx,ny));  
        }  
    }  
    return step[x][y];  
}  
int main(){  
    scanf("%d%d",&r,&c);  
    for(int i=0;i<=c+1;i++){  
        ④
    }   
    for(int i=0;i<=r+1;i++){  
        map[0][i]=map[r+1][i]=1e9;  
    }  
    for(int i=1;i<=r;i++){  
        for(int j=1;j<=c;j++){  
            scanf("%d",&map[i][j]);  
        }  
    }  
    for(int i=1;i<=r;i++){  
        for(int j=1;j<=c;j++){  
            ans=max(⑤,ans); 

②处应填

1
因为这个点也算一步

④处应填

map[i][0]=map[i][c+1]=1e9;

posted @ 2023-07-24 15:09  Eutopiax7  阅读(49)  评论(0)    收藏  举报