Scholomance Academy 读题训练,模拟(沈阳)

题意 :

  • 给出n组,每组包含一个字符(+/-)和一个数值
  • 对于任意一个数值x,如果x大于 θ \theta θ且为正,为TP;x大于 θ \theta θ且为负,为FP;x小于 θ \theta θ且为正,为FN;小于 θ \theta θ且为负,为TN
  • 现定义 T P R = T P / ( T P + F N ) TPR=TP/(TP+FN) TPR=TP/(TP+FN) F P R = F P / ( T N + F P ) FPR=FP/(TN+FP) FPR=FP/(TN+FP)
  • 可以发现TPR即所有字符为正的数值中属于TP的数量,FPR…
  • 可以根据取不同 θ \theta θ时TPR和FPR的值画出曲线,要求根据的ROC曲线,求出AUC(曲线下面积)

思路 :

  • AUC即取到FPR<=1的所有 θ \theta θ对应的TPR这条曲线下的面积,结合图例(分段常数函数)是多个矩形的面积,由若干段TPR*FPR相加,现在的问题是不知道哪里是分界点
  • 发现横纵坐标相乘 T P R ∗ d e l t a F P R = ( T P ∗ F P ) / ( ( T P + F N ) ∗ ( T N + F P ) ) TPR*deltaFPR=(TP*FP)/((TP+FN)*(TN+FP)) TPRdeltaFPR=(TPFP)/((TP+FN)(TN+FP)),又发现分母是所有字符正乘所有字符负,也就是说分母永远是常数,因此可以先将分子累加,最后除分母
  • 根据FPR的值从小到大(由定义公式推出 θ \theta θ从小到大)cmp函数是从小到大的,若值相同,’+'排在‘-’前,这样得到的一组坐标,它的横纵坐标都是递增的(非严格递增)),这样就可以计算了
  • 将+和-的值分别放在两个数组里,进行从小到大的cmp排序
  • a.end() - upper_bound(a.begin(), a.end(), b[i]) 返回大于b[i]的有几个
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
using namespace std;
typedef long long ll;

const int N = 1e6 + 10;

struct node
{
    
    
    char op;
    int num;
}a[N];

int n;

bool cmp(node a, node b)
{
    
    
    if (a.num == b.num)     // 如果数值相同则先出现符号为-的
        return a.op < b.op;
    return a.num < b.num;   // 先出现数值小的
}

int main()
{
    
    
//    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    
    int _ = 1;
//    cin >> _;

    while (_ -- )
    {
    
    
        scanf("%d", &n);

        getchar();      // 同下

        int po = 0, ne = 0;     // 代表正负总个数

        for (int i = 1; i <= n; i ++ )
        {
    
    
            scanf("%c%d", &a[i].op, &a[i].num);

            getchar();      // 换行也是char,scanf不会自动去掉换行

            if (a[i].op == '+') po ++ ;
            else ne ++ ;
        }

        sort(a + 1, a + n + 1, cmp);

        double ans = 0, t = 1;      // 乘积和 & TP*FP(>theta的正负乘积)

        for (int i = 1; i <= n; i ++ )
        {
    
    
            if (a[i].op == '-') ans += t / ne;  // 如果符号是'-',说明出现转折,乘积和(面积和)加上那部分
            else t = t - 1.0 / po;  // 如果符号是'+',
        }

        printf("%.10lf\n", ans);        // 误差小于等于1e-9
    }
    return 0;
}

题解二 :

  • 当theta变大时,tpr和fpr同时变大,注意到auc函数单调性(当r较大时 我们让FPR尽量大 这样可以让θ尽量大 使得TPR变大),且为若干斜率为0的直线(当r在一段较小的区间内波动时 FPR不会变化 如果让TPR尽量大 就令θ尽量大 此时TPR也不会变化),我们可以分割成一个个的矩形求面积,在遍历的时候将函数分成TN+FP段,每一段长度为1/(tn+fp)
    在这里插入图片描述
//#pragma GCC optimize(2)

#include<bits/stdc++.h>
//#define FAST ios::sync_with_stdio(false); cin.tie(0);
#define int long long
#define eps 1e-9

using namespace std;

const int MAXN = (int)1e6 + 5;

struct Node {
    
    
	char op;
	int val;
}a[MAXN];

int n, TNFP, TPFN, TP, FP;

bool cmp(Node& a, Node& b) {
    
    
	if(a.val == b.val) {
    
    
		if(b.op == '+') return false;
		if(a.op == '+') return true;
	}
	return a.val < b.val;
}

signed main()
{
    
    
	cin >> n;
	for(int i = 1; i <= n; i++) {
    
    
		cin >> a[i].op >> a[i].val;
		if(a[i].op == '+') {
    
    
			TPFN++;
		} else {
    
    
			TNFP++;
		}
	}
	sort(a + 1, a + 1 + n, cmp);
	TP = TPFN, FP = 0;
	int tot = 0;
	for(int i = 1; i <= n; i++) {
    
    
		if(a[i].op == '+') {
    
    		// 每遇到一个'+',tp就减少一个
			TP--;
		} else {
    
    		// 为'-'时遇到转折点,答案累加乘积(tp*fp)
			tot += TP;
		}
	}
	long double ans = 1.0 * tot / (TPFN * TNFP);
	printf("%.9Lf", ans);
	//cout << ans;
	return 0;
}

Guess you like

Origin blog.csdn.net/m0_51448653/article/details/121271619