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,所以
關係M1=M2-z帶入得
簡化:
這一部分也可以像上面一樣更新。
這道題寫著很麻煩,所以要細心,本來以為可以過,沒想到還是有兩處寫錯了。
下面是程式碼:
#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;
}