发现
有些题目可以卡检测点拿点分的!
题目描述
是一个由 个自然数(即非负整数)组成的数组。在此基础上,我们用数组 表示 的前缀最大值。如上所示, 定义为数组 中前 个数的最大值。 根据该定义易知 ,且随着 的增大, 单调不降。 此外,我们用 表示数组 中
个数的总和。现已知数组 ,我们想要根据 的值来反推数组 。 显然,对于给定的 , 的取值可能并不唯一。 试计算,在数组 所有可能的取值情况中,
的最大值和最小值分别是多少?
#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;
}
解法:
索引法:
用向量数组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