藍橋杯18屆第八題——日誌統計(尺取法,數據處理)

日誌統計

小明維護着一個程序員論壇。現在他收集了一份"點贊"日誌,日誌共有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函數參數——自定義比較函數,實現結構體組以結構體中的某個變量的大小對結構體組進行排序。
③、尺取法算法效率優化。

希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章