csp2021-09

每日编程

    • 第一题
    • 第二题

第一题

发现
有些题目可以卡检测点拿点分的!

题目描述
是一个由 个自然数(即非负整数)组成的数组。在此基础上,我们用数组 表示 的前缀最大值。

如上所示, 定义为数组 中前 个数的最大值。 根据该定义易知 ,且随着 的增大, 单调不降。 此外,我们用 表示数组 中
个数的总和。

现已知数组 ,我们想要根据 的值来反推数组 。 显然,对于给定的 , 的取值可能并不唯一。 试计算,在数组 所有可能的取值情况中,
的最大值和最小值分别是多少?

#include 
using namespace std;
void Guest(){
    int n;
    cin>>n;
    int B[100],maxn=0,minn=0;
    for(int i=0;i<n;i++){
        cin>>B[i];
    }
    minn=B[0];
    maxn=B[0];
    for(int j=1;j<n;j++){
        if(B[j]!=B[j-1]) minn=minn+B[j];
        maxn=maxn+B[j];
    }
    cout<<maxn<<endl;
    cout<<minn;

}
int main()
{
    Guest();
    return 0;
}

二刷:

#include 
#include 
using namespace std;
int B[100];

int main()
{
    int n;
    cin>>n;
    cin>>B[0];
    int sumin=B[0],sumax=B[0];
    for(int i=1;i<n;i++) {
            cin>>B[i];
        if(B[i]>B[i-1]){
            sumax=sumax+B[i];
            sumin=sumin+B[i];
        }
        else sumax=sumax+B[i-1];

    }
    cout<<sumax<<endl<<sumin;

}

第二题

题目如下
csp2021-09_第1张图片

解法:

索引法:
  用向量数组v[]存储各种值所在位置,向量v[i]中存储i值在数组a[]中的位置。向量v可以看作是各种值的下标索引。往往想这样的索引,会使得程序变得稍微难以理解。
  开始的时候,把整个A数组看作一段,所有程序中last=1。然后,取p=0,1,2,…,maxa(数组A元素的最大值)取划分数组A。如果a[i]位置置为0,那么如果原先a[i-1]=0并且a[i+1]=0则划分段数减一;如果a[i]位置置为0,那么如果原先a[i-1]<>0并且a[i+1]<>0则划分段数加一;其他情况则段数不变。数组book[]用来标记a[i]是否置为0,若book[i]=1则表示a[i]被置为0(操作使之为0)。开始时,数组A的两端置为0,所以程序中需要对数组book[]进行初始化。
  变量last表示上一次的划分数,变量t表示在上一次划分的基础上,再进行p变换后的划分数。
  这个解法程序虽然是二重循环,其实其时间复杂度是O(N)。


const int N=500000;
const int M=10000;
int num,mark[N+2];//mark记录出现元素是否为0
vector<int> m[M+1];//记录数组出现0~10000的对应下标队列
void countMostP(){
    int len,maxn=0;
    cin>>len;
    for(int i=1;i<=len;i++){//假设我们在首部和尾部添上了0,mark里对应的下标是1~len
            cin>>num;
            //把num出现在数组里的下标都记录下来
            m[num].push_back(i);
            //最大值选择
            if(num>maxn) maxn=num;
    }
    int ans=0;//记录我们的最终答案
    if((int)m[0].size()!=len){//当数组不全为0时
        memset(mark,0,sizeof mark);
        mark[0]=1;
        mark[len+1]=1;//假设头和尾都添上了0
        int last=1;//不全为0的情况当前至少有1个非零段
        for(int p=0;p<=maxn;p++)//从p=0的情况开始考虑p,也就是初始状态
            {
                int now=last;
                if((int)m[p].size()!=0){//这个p是存在于数组的数才考虑
                    for(int i=0;i<(int)m[p].size();i++){
                        mark[m[p][i]]=1;//元素已考虑,置零
                        if(mark[m[p][i]-1]&&mark[m[p][i]+1]) now--;//如果当前元素的左右元素都被置零过了,这一轮里该元素也被置零了,说明段数减少了
                        else if(mark[m[p][i]-1]==0&&mark[m[p][i]+1]==0) now++;//出现了划分,段数增加
                    }
                }
                //一轮出来后监测最大情况是否有变
                if(now>ans) ans=now;
                last=now;
        }
    }
    cout<<ans;
}

int main()
{

    countMostP();
    return 0;
}

学到
int book[N + 2];
memset(book, 0, sizeof book);
初始化的方法,头文件cstring

解法三:差分法
  借用岛屿情况来分析这个题。考虑p足够大的情况,所有的数都被海水淹没了,只有0个岛屿。然后,海平面逐渐下降,岛屿数量出现变化。每当一个凸峰出现,岛屿数就会多一个;每当一个凹谷出现,原本相邻的两个岛屿就被这个凹谷连在一起了,岛屿数减少一个。使用数组cnt[],cnt[i] 表示海平面下降到i时,岛屿数量的变化。
  差分法是最简洁的解题程序。数组元素d[i]中存储该元素被替换为0时,划分数变化的差分值。最大值则只需要从其前缀和(程序中实际为后缀和)中找出最大值就是所要的结果。
  程序代码中,STL算法函数unique()用来去除相邻重复的元素。
  语句“a[0] = a[n + 1] = 0;”用来设置边界值,起辅助计算作用,可以简化程序代码。

#include 
using namespace std;
const int N = 500000;
const int M = 10000;
int a[N + 2], d[M + 1];

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    a[0] = a[n + 1] = 0;

    n = unique(a, a + n + 2) - a - 1;//unique的作用是“去掉”容器中相邻元素的重复元素(不一定要求数组有序),它会把重复的元素添加到容器末尾(所以数组大小并没有改变),而返回值是去重之后的尾地址

    memset(d, 0, sizeof d);
    for (int i = 1; i < n; i++)
        if (a[i - 1] < a[i] && a[i] > a[i + 1]) d[a[i]]++;
        else if (a[i - 1] > a[i] && a[i] <a[i + 1]) d[a[i]]--;

    int ans = 0, sum = 0;   // 差分前缀和即为答案
    for (int i = M; i >= 1; i--)
        sum += d[i], ans = max(ans, sum);

    printf("%d\n", ans);
    return 0;
}


为什么要去掉相邻重复元素?因为2133321和21321在这里应用效果是一样的。

学到
头文件bits/stdc++.h很万能
unique()函数可以去掉相邻元素的重复元素,将其挪到队尾
unique()函数返回去除相邻重复元素后的尾地址


主要参考和引用了海岛大佬的博文

二刷:

#include 
#include 
using namespace std;
int A[500002];

int main()
{
    int n;
    cin>>n;
    set<int> num;
    A[0]=0,A[n+1]=0;
    for(int i=1;i<=n;i++){
        cin>>A[i];
        num.insert(A[i]);
    }
    set<int>::iterator it;
    int nmax=0;
    for(it=num.begin();it!=num.end();it++){
            int now=0;
        for(int i=1;i<=n;i++){
            if(*it<=A[i]&&A[i+1]<*it) now++;
        }
        if(now>nmax) nmax=now;


    }
    cout<<nmax;
    return 0;


}

小菜鸡先以解决问题为主,用最简单最直观的办法,AC出70

你可能感兴趣的:(算法,排序算法,c++)