1. 程式人生 > >1785 資料流中的演算法(思路題,細心點就行了)

1785 資料流中的演算法(思路題,細心點就行了)

51nod近日上線了使用者滿意度檢測工具,使用高階人工智慧演算法,通過使用者訪問時間、滑鼠軌跡等特徵計算使用者對於網站的滿意程度。

現有的統計工具只能統計某一個視窗中,使用者的滿意程度的均值。夾克老爺想讓你為統計工具新增一個新feature,即在統計均值的同時,計算視窗中滿意程度的標準差和中位數(均值需要向下取整)。
Input
第一行是整數n與k,代表有n次操作,時間視窗大小為k。
(1 <= n <= 10^6, 1 <= k <= 100)

接下來的n行,每行代表一次操作。操作有“使用者訪問”、“查詢均值”、“查詢方差”、“查詢中位數”四種。每行的第一個數代表操作型別。

運算元1:使用者訪問
輸入格式:<1, v>
使用者的滿意度v為閉區間[0, 100]中的任意整數。使用者每訪問一次,資料更新,移動統計視窗。

運算元2:查詢均值
輸入格式:<2>
統計視窗內的使用者滿意度的均值。

運算元3:查詢方差
輸入格式:<3>
統計視窗內使用者滿意度的方差

運算元4:查詢中位數
輸入格式:<4>
統計視窗內使用者滿意度的中位數

p.s. 在有查詢請求時,視窗保證不為空
p.s.s. 有查詢請求時,視窗可能不滿
Output
對於“查詢均值”、“查詢方差”、“查詢中位數”操作的結果,輸出保留兩位小數。
Input示例
12 3
1 1
1 2
1 3
2
3
4
1 4
1 5
1 6
2
3
4
Output示例
2.00
0.67
2.00
5.00
0.67
5.00

思路:說幾個重要的點,他最多統計的是最近的k個使用者的資料,時間更早的使用者的資料會被擠掉,有個很坑的點給忘記了(這是我們錯的兩個點中的一個),均值要向下取整。其他的就按照題目描述即可。

還有一個點是在記錄偶數的平均值的時候,記錄的兩個資料,有偏差。

歐對了,忘了一點,如何求解方差,方差這裡寫圖片描述

如何通過:以前的方差求解現在的方差?
我們只在這裡記錄方差上面的平方和(ans),設現在的平均數(M1)與上一個的平均數(M2),現在的ans為ans1,上一個的ans為ans2,就有關係M1=M2-z,所以

ans1=(x1M1)2+.
....+(xnM1)2

關係M1=M2-z帶入得
ans1=(x1M2+z)2+.....+(xnM2+z)2
簡化:
ans1=(x1M2)2+.....+(xnM2)2+nzz+2z((x1M2)+....+(xnM2))
(x1M2)+....+(xnM2)這一部分也可以像上面一樣更新。

這道題寫著很麻煩,所以要細心,本來以為可以過,沒想到還是有兩處寫錯了。

下面是程式碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int max2=1e6+9;
#define mem(a,b) memset(a,b,sizeof(a))
#define PI acos(-1.0)

int a[max2],b[109];
double ans1,ans2,ping,ans3;//下面加‘_’的與這意思一樣,ans1記錄方差上面的平方和,ans2記錄xi-M的和,ans3最後k項使用者的滿意度,ping記錄平均值
int inq;
int n,k,in;
void updata(int x)
{
    inq++;
    b[x]++;
    double ping_,ans1_,ans2_,ans3_;//這是最新的資料
    if(inq>k)//超過視窗的限制
    {
        //裡面寫的就有點複雜了,還是你慢慢看吧
        b[a[inq-k]]--;
        ping_=(ans3-a[inq-k]*1.0+x*1.0)/(k*1.0);
        double cha=ping-ping_;
        ans2_=ans2-(a[inq-k]-ping)+x-ping_+cha*(k-1);
        ans1_=ans1-(a[inq-k]-ping)*(a[inq-k]-ping)+(x-ping_)*(x-ping_)+cha*cha*(k-1)+2.0*cha*(ans2-(a[inq-k]-ping));
        ans3_=ans3-a[inq-k]+x;
        //在這裡將資料更新一下
        ping=ping_;
        ans3=ans3_;
        ans1=ans1_;
        ans2=ans2_;

    }
    else//與上面的相似
    {
        ping_=(ans3+x*1.0)/inq;
        double cha=(ping-ping_);
        ans2_=ans2+x-ping_+cha*(inq-1);
        ans1_=ans1+cha*cha*(inq-1)+2*ans2*cha+(x-ping_)*(x-ping_);
        ans3_=ans3+x;
        ping=ping_;
        ans3=ans3_;
        ans1=ans1_;
        ans2=ans2_;
    }
    a[inq]=x;
}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        ans1=0,ans2=0,ping=0,inq=0,ans3=0;
        mem(b,0);
        while(n--)
        {
            int p,x;
            scanf("%d",&p);
            if(p==1)//更新所有資料
            {
                scanf("%d",&x);
                updata(x);
            }
            else if(p==2)//均值,要向下取整(這是第一處錯誤)
            {
                in=min(inq,k);
                int p=ans3/(in*1.0);
                printf("%d.00\n",p);
            }
            else if(p==3)//方差
            {
                in=min(inq,k);
                printf("%.2lf\n",ans1/(in*1.0));
            }
            else if(p==4)
            {
                in=min(inq,k);
                double an;
                if(in%2)//中位數有一個
                {
                    int s=0;
                    for(int i=0; i<=100; i++)
                    {
                        s+=b[i];
                        if(s>in/2)
                        {
                            an=i;
                            break;
                        }
                    }
                }
                else//中位數有兩個,就是這種情況記錄錯了(這是第二處錯誤),
                {
                    an=0;
                    int s=0,flag=1;
                    for(int i=0; i<=100; i++)
                    {
                        if(b[i]==0) continue;
                        s+=b[i];
                        if(s>=in/2&&flag)//這裡只能記錄一次,當初就是這裡錯了,想哭
                        {
                            an+=i;
                            flag=0;
                        }
                        if(s>=in/2+1)
                        {
                            an+=i;
                            break;
                        }
                    }
                    an/=2.0;
                }
                printf("%.2lf\n",an);
            }
        }
    }
    return 0;
}