CCF CSP 2020-12-2 期末预测之最 佳阈值 C语言100分

CCF CSP2020-12-2 期末预测之最佳阈值 C语言100分

(发布的时候说我【最佳】阈值夸张,不符合发布题目的规范,可这个是人家的题目啊啊啊 orz)
期末预测之最佳阈值 代码长度1.629KB C语言 正确 100分 耗时31ms 空间使用5.386MB 完成时间11-12 11:01

应该会慢慢更新,0算法基础极限挑战今年12月得CSP。(尽量)坚持每天一道

(这道题弄了我好几天,感觉12月的CSP要凉)

刚开始看到这道题想到利用双for循环去判断,果不其然超时了,而且只得了40分,也就是说70%的数据都没有跑完就超时了。所以需要简化算法,如果所有阈值都按顺序排好的话就不用每次循环一遍。避免使用双循环,使用快速排序。

typedef struct Num   //定义结构体将阈值 结果关联起来
{
		int x;
		int y;
}NUM;
int quicksort(NUM num[], int left, int right)
{
	int temp,i,j,target;
	i=left;
	j=right;
	temp=num[i].x;
	target=num[i].y;
	if(left>right)    // i<j时,循环进行 否则返回 
	{
		return 0;
	}
	while(i!=j)
	{
		while(num[j].x>=temp&&j>i)
		{
			j--;
		}
		if(num[j].x<temp&&j>i)   //if(num[j]<temp&&j>i)因为跳出 while(num[j]>temp&&j>i)得条件是num[j]>temp,所以可以省去 
		{
			num[i++]=num[j];
		}
		while(num[i].x<=temp&&j>i)
		{
			i++;
		}
		if(num[j].x<temp&&j>i)
		{
			num[j--]=num[i];
		}
	}
	num[i].x=temp;
	num[i].y=target;
	quicksort(num,left,i-1);
	quicksort(num,i+1,right);
	return 0;
}

但按这样还是超时了(可能是我电脑太差了),总之还是只得了70分,那么就想到了简化计算每个阈值的正确次数的算法。可以将相同阈值合并,用n0和n1来统计本阈值下正确和错误的次数。

typedef struct s
{
		int x; //阈值
		int n0;  //x时结果为0的个数
		int n1;  //x时结果为1的个数
		int cnt0;  //小于x且结果为0的个数
		int cnt1;  //大于等于x且结果为1的个数
		int cnt;   //cnt0+cnt1
}S;
	s[0].x=num[0].x;
	if(num[0].y==0)
	{
		s[0].n0++;
	}
	else s[0].n1++;
	for(i=1,j=0;i<n;i++)
	{
		if(num[i].x!=num[i-1].x)
		{
			j++;
		}
		s[j].x=num[i].x;
		if(num[i].y==0)
		{
			s[j].n0++;
		}
		else s[j].n1++;  //共j+1个数 
	}
	printf("\n%d\n",j);
	printf("统计后:\n");
	for(i=0;i<j+1;i++)
	{
		printf("%-10d\t%-10d\t%-10d\n",s[i].x,s[i].n0,s[i].n1);
	}

重点来了
在手算的时候发现最终预测正确的个数可以分为:小于阈值且结果为0和大于等于阈值且1。
若取阈值为s[i].x(已经排序且统计过对应1和0个数的结构体),那么小于s[i].x且结果为0的个数就可以认为是小于s[i-1].x且结果为0的个数+s[i-1].n0的个数。类似于数列求和S[n]=S[n-1]+a[n]
同时,大于s[i].x且结果为1的个数就可以认为是大于等于s[i+1].x且结果为1的个数+s[i].n1的个数。类似于S[n+1]=S[n]-a[n]的反向S[n]=S[n+1]+a[n+1]

上述可以分别用2个for循环来表示

	s[0].cnt0=0;   //小于最小阈值且结果为0的情况为0
	s[j].cnt1=s[j].n1;  //大于等于最大阈值且结果为1的情况只有等于最大阈值
	for(i=1;i<j+1;i++)
	{
		s[i].cnt0=s[i-1].cnt0+s[i-1].n0;
	}
	for(i=j-1;i>=0;i--)
	{
		s[i].cnt1=s[i+1].cnt1+s[i].n1;
	}
	for(i=0;i<j+1;i++)
	{
		s[i].cnt=s[i].cnt0+s[i].cnt1;
	}

这是最终提交100分的代码

#include<stdio.h>
typedef struct Num
{
		int x;
		int y;
}NUM;
typedef struct s
{
		int x;
		int n0;
		int n1;
		int cnt0;
		int cnt1;
		int cnt; 
}S;
int quicksort(NUM num[], int left, int right);
int main()
{
	int n,i,j,left,right,pos,temp;
	scanf("%d",&n);
	NUM num[n];
	S s[n];
	for(i=0;i<n;i++)
	{
		scanf("%d %d",&num[i].x,&num[i].y);
		s[i].cnt0=s[i].cnt1=s[i].cnt=s[i].n0=s[i].n1=s[i].x=0;
	}
	left=0;
	right=n-1;
	quicksort(num,left,right);
	s[0].x=num[0].x;
	if(num[0].y==0)
	{
		s[0].n0++;
	}
	else s[0].n1++;
	for(i=1,j=0;i<n;i++)
	{
		if(num[i].x!=num[i-1].x)
		{
			j++;
		}
		s[j].x=num[i].x;
		if(num[i].y==0)
		{
			s[j].n0++;
		}
		else s[j].n1++;  //共j+1个数 
	}
	s[0].cnt0=0;
	s[j].cnt1=s[j].n1;
	for(i=1;i<j+1;i++)
	{
		s[i].cnt0=s[i-1].cnt0+s[i-1].n0;
	}
	for(i=j-1;i>=0;i--)
	{
		s[i].cnt1=s[i+1].cnt1+s[i].n1;
	}
	for(i=0;i<j+1;i++)
	{
		s[i].cnt=s[i].cnt0+s[i].cnt1;
	}
	pos=s[0].x;
	temp=s[0].cnt;
	for(i=1;i<j+1;i++)
	{
		if(s[i].cnt>=temp)
		{
			temp=s[i].cnt;
			pos=s[i].x;
		}
	}
	printf("%d",pos);
	return 0;
}
int quicksort(NUM num[], int left, int right)
{
	int temp,i,j,target;
	i=left;
	j=right;
	temp=num[i].x;
	target=num[i].y;
	if(left>right)    // i<j时,循环进行 否则返回 
	{
		return 0;
	}
	while(i!=j)
	{
		while(num[j].x>=temp&&j>i)
		{
			j--;
		}
		if(num[j].x<temp&&j>i)   //if(num[j]<temp&&j>i)因为跳出 while(num[j]>temp&&j>i)得条件是num[j]>temp,所以可以省去 
		{
			num[i++]=num[j];
		}
		while(num[i].x<=temp&&j>i)
		{
			i++;
		}
		if(num[j].x<temp&&j>i)
		{
			num[j--]=num[i];
		}
	}
	num[i].x=temp;
	num[i].y=target;
	quicksort(num,left,i-1);
	quicksort(num,i+1,right);
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

JY_0329

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

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

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

打赏作者

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

抵扣说明:

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

余额充值