CCF202109-2非零段划分(c++)

题目:

 解法:

解法一:(暴力求解法,70分)

暴力求解,就不给解释了,想看的稍微看一看即可。

#include <iostream>
using namespace std;
int max(int a[],int n) {
	int x=0;
	for(int j=0; j<n; j++) {
		if(a[j]>=x) x=a[j];
	}
	return x;
}
bool quanling(int a[],int n) {
	for(int i=0; i<n; i++) {
		if(a[i]!=0) return false;
	}
	return true;
}
int main() {
	int n;
	cin>>n;
	int a[n];
	for(int i=0; i<n; i++) cin>>a[i];
	int Max=max(a,n);
	if(quanling(a,n)) {
		cout<<0;
	} else {
		int count=0;
		for(int p=1; p<=Max; p++) {
			int temp=0;
			for(int j=0; j<n-1; j++) {
				if(j>=1) {
					if(a[j]<p&&a[j+1]>=p) temp++;
					else if(a[j]<p&&a[j+1]<p) continue;
					else if(a[j]>=p&&a[j+1]<p) continue;
					else if(a[j]>=p&&a[j+1]>=p) continue;
				} else {
					if(a[j]<p&&a[j+1]<p) continue;
					else if(a[j]<p&&a[j+1]>=p) temp++;
					else if(a[j]>=p) temp++;
				}

			}
			if(temp>=count) count=temp;
		}
		cout<<count;
	}
	return 0;
}

解法二:(索引法,100分)

首先要找到一个向量,这个向量可以存储数组a中值的位置。即向量v[i]中存储的便是i在数组a中的位置。此时向量v可以看成数组a中每个值的索引。然后还要找到一个数组,此数组与数组a的值一一对应用作标记,即a[i]的值一旦被置为0,此数组[i]则被置为1.

开始时,要在数组a前后两端置0,并且对标记数组进行初始化。程序中有一last(上一次划分的段数)。取p为0到maxa(数组a的最大值),每个p则代表小于等于p的值置0。找到值为p的位置将标记数组记为1,当此位置左右均已置为0时划分段树减一,当此位置左右均不为0时则划分段加一。然后对目前结果,上一次划分段数last以及此次划分段数取最大值记为目前结果。最后将此次划分段数记为上一次划分段数last。

#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
const int N = 500000;
const int M = 10000;
int a, book[N + 2];
vector <int> v[M + 1];
int main() {    //索引法
	int n, maxa = 0;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a;
		maxa = max(maxa, a);
		v[a].push_back(i);   //v[a]中存储的是a在数组a中的位置
	}
	int ans = 0;   //结果,最大段数
	if (v[0].size() != n) {    //如果数组a并非全0
		memset(book, 0, sizeof book);   //将book数组全部置零
		book[0] = book[n + 1] = 1;//数组两端已经置零则被标记为1
		int last = 1;
		for (int p = 0; p <= maxa; p++) {
			if (v[p].size() != 0) {
				int tem = last;
				for (int i = 0; i < (int)v[p].size(); i++) {
					book[v[p][i]] = 1;
					if (book[v[p][i] - 1] && book[v[p][i] + 1]) tem--;
					else if (book[v[p][i] - 1] == 0 && book[v[p][i] + 1] == 0) tem++;
				}
				ans = max(ans, max(last, tem));
				last = tem;
			}

		}

	}
	//数组a全0
	cout << ans;
	return 0;
}

顺手给出一个关于vector的网址:

https://blog.csdn.net/whu_little_hack/article/details/112980332?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163790238216780271985216%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163790238216780271985216&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-112980332.pc_search_es_clickV2&utm_term=vector%E5%AE%B9%E5%99%A8&spm=1018.2226.3001.4187

解法三:(差分法,100分)

用差分法可以借助岛屿情况来分析。p足够大,所有的数(岛屿)被海水淹没,只有0个岛屿(划分段)。当海平面逐渐下降,岛屿数量出现变化。每当出现一个凸峰,岛屿数加一,每当出现一个凹谷,原本相邻的两个岛屿就被这个凹谷连在了一起,岛屿数减一。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int N=500000;
const int M=10000;
int a[N+2],d[M+1];
int main() {  //差分法
	int n;
	cin>>n;
	for(int i=1; i<=n; i++) cin>>a[i];
	a[0]=a[n+1]=0;
	n=unique(a,a+n+2)-a-1;    //最后一位坐标 
	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);
	}
	cout<<ans;
	return 0;
}

总的来说,差分法就是先消去相邻的重复元素,再从unique后的数组第一位到最后一位,依次比较数字与相邻数的大小,并对d操作。(解释一下下:如果此时是从d[i]+d[i+1]+........  则表示海平面为i时的岛屿数量,即p=i时的划分段数。)

这里说明一下unique,unique()函数是用来去除相邻重复的元素,返回的结果为去除相邻重复元素后的数组(将去除相邻元素后的数组放在新数组的前面部分,新数组的后面部分保持不变)

放个代码解释一下:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main() {
	int n;
	cin>>n;
	int a[n];
	for(int i=0; i<n; i++) cin>>a[i];
	cout<<"原数组:"<<endl;
	for(int i=0; i<n; i++) cout<<a[i]<<" ";
	unique(a,a+n);
	cout<<endl<<"unique后的数组:"<<endl;
	for(int i=0; i<n; i++) cout<<a[i]<<" ";
	cout<<endl;
	cout<<"unique后的数组长度"<<(unique(a,a+n)-a);    //unique(a,a+n)-a为求数组长度 
	return 0;
}

运行情况:

还有一特殊情况(本人认为unique后数组最后几位0合归为一位):

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a_dafei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值