CCF CSP第2题总结(持续更新中)

第二题难度每年不一,简单的年份理解清题意、想好每一步干什么、用什么数据结构存储,然后直接翻译就可以了,如果要加难度,基本都是时间复杂度方面,常见的方法有动态规划/差分/前缀和等等

目录

2021-09-2 非零段划分

2021-06-2 领域均值

2020-09-2 风险人群筛查

2020-06-2 稀疏向量

2019-12-02 回收站选址

2019-09-2 小明种苹果(续)

2021-09-2 非零段划分

法一:暴力法 for(p=0;p<=maxp;p++) 对每个p求对应的非零段数量,记录最大值 70分

法二:差分法:以变化的视角来看待,观察p每次-1(或+1)时,非零段个数的变化,可以发现影响非零段数量的本质是“凸”和“凹”的情况

思想:将数组中每个值看成对应高度的“山峰”,题中的p相当于“海平面”,所求就是高于海平面的“岛屿”(由若干连续的山峰组成)个数

分两种情况:

两高夹一矮 则当海平面下降到矮峰下面时,就会连起来,岛屿数-1

两矮夹一高 则当海平面下降到高峰下面时,岛屿数+1

将凸和凹的组数保存到ans[]数组中,ans的索引代码山峰的高度,最后在ans数组叠加的过程中记录最大值即可

#include
using namespace std;
int a[500005];
int cnt[10005];

int main(){
	int n;
	cin>>n;
	a[0]=0;for(int i=1;i<=n;i++)cin>>a[i];a[n+1]=0;
	n=unique(a,a+n+2)-a;
	for(int i=1;ia[i+1])
			cnt[a[i]]++;
		else if(a[i-1]>a[i]&&a[i]0;i--){
		sum+=cnt[i];
		ans=max(ans,sum);
	}
	cout<


2021-06-2 领域均值

二维前缀和

参考了这篇大佬的博客 ​​​​​​ (206条消息) CSP-2邻域均值_Vanghua的博客-CSDN博客,不过自己习惯用x表示行(i),用y表示列(j),本质其实一样的。

第一个循环中i,j的含义是矩阵的右下角

第二个循环中i,j是矩阵的中心,(xl,yt)是矩阵左上角,(xr,yb)是矩阵右下角

#include 
using namespace std;
int ar[601][601], sum[601][601];

int main() {
	int n, l, r;
	float t;
	cin >> n >> l >> r >> t;
	for(int i = 1; i <= n; i ++) 
		for(int j = 1; j <= n; j ++) {
			cin >> ar[i][j];
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + ar[i][j];
		}
	
	int xl, xr, yt, yb, sumVal, cnt = 0;
	float avgVal;
	for(int i = 1; i <= n; i ++) 
		for(int j = 1; j <= n; j ++) {
			yt = j - r >= 1 ? j - r : 1;
			yb = j + r <= n ? j + r : n;
			xl = i - r >= 1 ? i - r : 1;
			xr = i + r <= n ? i + r : n;
			sumVal = sum[xr][yb] - sum[xl - 1][yb] - sum[xr][yt-1] + sum[xl - 1][yt - 1];
			avgVal = (float)sumVal / ((yb - yt + 1) * (xr - xl + 1));
			if(avgVal <= t)
				cnt ++;
		}
	cout << cnt;
}


2020-09-2 风险人群筛查

没用什么算法

理解清题意,直译,f1记录是否经过,f2记录是否停留,pass表示经过人数,stay表示停留人数,内循环结束后根据f1,f2进行pass++,stay++

#include
using namespace std;
const int maxn=10005;
int x[maxn],y[maxn];
	
int main(){
	int n,k,t,xl,yd,xr,yu;
	cin>>n>>k>>t>>xl>>yd>>xr>>yu;
	int stay=0,pass=0;
	for(int j=0;j>x[i]>>y[i];
			if(xl<=x[i]&&xr>=x[i]&&yd<=y[i]&&yu>=y[i])
				{
					cnt++; 
					f1=1;
					if(cnt>=k)f2=1;
				}
			else cnt=0;
			
		}
		if(f1)pass++;
		if(f2)stay++;
	}
	cout<


2020-06-2 稀疏向量

遍历其中一个向量的非零项索引,查找另一个向量中对应的地方是否也为非零项(相当于查看x[N]或y[N]中是否也有该索引),若有则相乘并累加

其中优化时间复杂度的地方在于,观察可以发现题目中给出的索引都是从小到大排列的,因此在查找的过程当中,不需要找到尾部,一旦大于索引就可以终止查找,并且不需要每次从头查找,只要在每次查找完用一个全局变量标记当前位置,下次接着往后找就行,因此时间复杂度是线性的

#include
using namespace std;
int n,a,b;
const int N=5e5+5;
int x[N],y[N];
long u[N],v[N];
long long ans=0;
int w=0;

int search(int f,int c){
	for(int k=w;kf){
			w=k;
			return -1;
		}
	}
	return -1;
}

int main(){
	cin>>n>>a>>b;
	for(int i=0;i>x[i]>>u[i];
	for(int j=0;j>y[j]>>v[j];
	if(a<=b){
		for(int i=0;i


2019-12-02 回收站选址

没用什么算法

直译,先判断能否作为回收站,然后判断能得几分,用f[]记录各个分数的回收站个数

#include
using namespace std;
#define forn(i,n) for(int i=0;i>n;forn(i,n){cin>>p[i].x>>p[i].y;}
    forn(i,n){
        if(find(p[i].x-1,p[i].y)&&find(p[i].x,p[i].y+1)
        &&find(p[i].x+1,p[i].y)&&find(p[i].x,p[i].y-1)){
            int c=0;
            if(find(p[i].x-1,p[i].y-1))c++;
            if(find(p[i].x-1,p[i].y+1))c++;
            if(find(p[i].x+1,p[i].y+1))c++;
            if(find(p[i].x+1,p[i].y-1))c++;
            f[c]++;
        }
    } 
    forn(i,5)cout<


2019-09-2 小明种苹果(续)

没用什么算法

直译

#include
using namespace std; 
#define int long long 
int m,n;
int a[1005],b[1005]={0};
bool flag[1005]={0};
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    int total=0,e=0,d=0;
    for(int i=0;i>m;
        cin>>a[i];
        for(int j=1;j>tmp;
             if(tmp<=0)a[i]+=tmp;
             else
             {
                if(tmp=1&&i<=n-1&&flag[i]&&flag[i-1]&&flag[i-2])
        e++;       
    } 
    if(flag[0]&&flag[1]&&flag[n-1])
    e++;
    if(flag[n-2]&&flag[0]&&flag[n-1])
    e++;
    cout<

你可能感兴趣的:(CSP,c++,算法,数据结构)