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;
}