日誌統計
小明維護着一個程序員論壇。現在他收集了一份"點贊"日誌,日誌共有N行。
其中每一行的格式是:ts id
表示在ts時刻編號id的帖子收到一個"贊"。
現在小明想統計有哪些帖子曾經是"熱帖"。如果一個帖子曾在任意一個長度爲D的時間段內收到不少
於K個贊,小明就認爲這個帖子曾是"熱帖"。
具體來說,如果存在某個時刻T滿足該帖在[T, T+D)這段時間內(注意是左閉右開區間)收到不少於K個
贊,該帖就曾是"熱帖"。
給定日誌,請你幫助小明統計出所有曾是"熱帖"的帖子編號。
【輸入格式】
第一行包含三個整數N、D和K。
以下N行每行一條日誌,包含兩個整數ts和id。
對於50%的數據,1 <= K <= N <= 1000
對於100%的數據,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
【輸出格式】
按從小到大的順序輸出熱帖id。每個id一行。
【輸入樣例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
【輸出樣例】
1
3
題意
①、熱帖定義:存在某個時刻T滿足該帖在[T, T+D)這段時間內(注意是左閉右開區間)收到不少於K個
贊,該帖就曾是"熱帖"。
②、統計出所有曾是"熱帖"的帖子編號。
解題思路
題目要求在給定的時間區間([T, T+D))範圍內,統計出曾是"熱帖"的帖子編號。
很顯然,既然是在給定區間內做統計,那就要用到尺取法的思想來解題。
ps:若對尺取法不太熟悉的朋友,可以點擊下面鏈接學習、鞏固。
尺取法詳解與練習
數據處理
尺取法的應用要在有序的條件下進行,但題目給我們的是無序的 ts 與 id ,那如何巧妙地將 ts 與 id 的數據進行處理呢?
①、ts 與 id 之間是有聯繫、不可分割存儲,這裏利用結構體的形式將 ts 與 id 聯繫在一起。
struct node{
int ts;
int id;
};
②、vector工具存儲N組 ts 與 id 結構體,並得到結構體組,方便進行排序。
注意:vector最好不要開過大空間,否則將vector中會出現多餘的元素,導致下面從小到大的排序出現錯誤,這裏只需開到N就足夠了。
vector<node> ts_id(N);
③、尺取法是在時間 ts 上應用,以 ts 的大小來對結構體組的進行排序。
//自定義比較函數
bool cmp(node r1, node r2)
{
return r1.ts<r2.ts;
}
//將vector中的元素按ts從小到大排序
sort(ts_id.begin(), ts_id.end(), cmp);
通過自定義比較函數的方式,能夠將結構體組以結構體中的某個變量的大小對結構體組進行排序。
不得不說,sort函數是很實用的排序函數!
尺取法的運用
代碼一:
每改變一次左指針,都重新統計[ts_id[r].ts ~ ts_id[l].ts+D)區間上的id出現次數。
這種方法效率並不高,複雜度爲O(n2)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
int ts;
int id;
};
bool cmp(node r1, node r2)
{
return r1.ts<r2.ts;
}
int main()
{
ll N, D, K;
cin>>N>>D>>K;
//vector不要開過大空間
//否則將vector中會出現多餘的元素
//從小到大的排序出現錯誤
vector<node> ts_id(N);
for(int i=0; i<N; i++)
cin>>ts_id[i].ts>>ts_id[i].id;
//將vector中的元素按ts從小到大排序
sort(ts_id.begin(), ts_id.end(), cmp);
//每改變一次左指針,都重新統計[ts_id[r].ts~ts_id[l].ts+D)區間上的id出現次數
set <int> ans;
for(int l=0; l<N; l++)
{
map <int, int> cnt;
for(int r=l; ts_id[r].ts<ts_id[l].ts+D && r<N; r++)
{
cnt[ts_id[r].id]++;
if(cnt[ts_id[r].id]==K)
{
ans.insert(ts_id[r].id);
break;
}
}
}
set<int>::iterator iter;
for(iter=ans.begin(); iter!=ans.end(); iter++)
cout<<*iter<<endl;
return 0;
}
代碼二:
在ts的時間軸上,區間[ts_id[r].ts ~ ts_id[l].ts+D)進行移動。
避免了每次移動左指針後都要重新統計 [ts_id[r].ts ~ ts_id[l].ts+D)區間上的id出現次數。
複雜度爲O(n)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
int ts;
int id;
};
bool cmp(node r1, node r2)
{
return r1.ts<r2.ts;
}
int main()
{
ll N, D, K;
cin>>N>>D>>K;
//vector不要開過大空間
//否則將vector中會出現多餘的元素
//從小到大的排序出現錯誤
vector<node> ts_id(N);
for(int i=0; i<N; i++)
cin>>ts_id[i].ts>>ts_id[i].id;
//將vector中的元素按ts從小到大排序
sort(ts_id.begin(), ts_id.end(), cmp);
//以區間[ts_id[r].ts ~ ts_id[l].ts+D)進行移動
set <int> ans;
map <int, int> cnt;
for(int l=0, r=0; l<N; l++)
{
while(r<N && ts_id[r].ts<ts_id[l].ts+D)
{
cnt[ts_id[r].id]++;
if(cnt[ts_id[r].id]==K)
ans.insert(ts_id[r].id);
r++;
}
//左指針出現的id去掉
//左指針向右移動
cnt[ts_id[l].id]--;
}
set<int>::iterator iter;
for(iter=ans.begin(); iter!=ans.end(); iter++)
cout<<*iter<<endl;
return 0;
}
總結
①、使用vector存儲多組結構體,形成結構體組,便於排序。
②、通過設計sort函數參數——自定義比較函數,實現結構體組以結構體中的某個變量的大小對結構體組進行排序。
③、尺取法算法效率優化。
希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。