Telefraud(電信詐騙) remains a common and persistent problem in our society. In some cases, unsuspecting victims lose their entire life savings. To stop this crime, you are supposed to write a program to detect those suspects from a huge amount of phone call records.算法
A person must be detected as a suspect if he/she makes more than K short phone calls to different people everyday, but no more than 20% of these people would call back. And more, if two suspects are calling each other, we say they might belong to the same gang. A makes a short phone call to B means that the total duration of the calls from A to B is no more than 5 minutes.數組
Each input file contains one test case. For each case, the first line gives 3 positive integers $K (≤500$, the threshold(閾值) of the amount of short phone calls), $N (≤10^3$, the number of different phone numbers), and $M (≤10^5$, the number of phone call records). Then $M$ lines of one day's records are given, each in the format:網絡
caller receiver duration
where caller
and receiver
are numbered from 1 to N, and duration
is no more than 1440 minutes in a day.函數
Print in each line all the detected suspects in a gang, in ascending order of their numbers. The gangs are printed in ascending order of their first members. The numbers in a line must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.this
If no one is detected, output None
instead.spa
5 15 31 1 4 2 1 5 2 1 5 4 1 7 5 1 8 3 1 9 1 1 6 5 1 15 2 1 15 5 3 2 2 3 5 15 3 13 1 3 12 1 3 14 1 3 10 2 3 11 5 5 2 1 5 3 10 5 1 1 5 7 2 5 6 1 5 13 4 5 15 1 11 10 5 12 14 1 6 1 1 6 9 2 6 10 5 6 11 2 6 12 1 6 13 1
3 5 6
Note: In sample 1, although 1
had 9 records, but there were 7 distinct receivers, among which 5
and 15
both had conversations lasted more than 5 minutes in total. Hence 1
had made 5 short phone calls and didn't exceed the threshold 5, and therefore is not a suspect.code
5 7 8 1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1 7 1 2 1 1 3 1 1
None
這個題目的本意就是在全部嫌疑人組成的網絡中找到每個幫派的成員,而每個幫派能夠認爲是一個連通份量,這樣就可使用DFS來進行求解,同時也能夠認爲是一個集合,這樣就可使用並查集的方式來求解,這裏給出兩種不一樣的實現方法。orm
DFS和並查集的預處理都是同樣的,主要目的就是找出全部的嫌疑人,這樣才方便在全部嫌疑人所構成的網絡中進行查找和合並。首先咱們使用二維數組G保存該圖的邊權,並注意到此圖是一個有向圖,邊權在輸入的時候得累計,使用frequency和callback數組分別表示每個人打電話不超過5分鐘的次數和每個嫌疑人在打的不超過5分種通話中,接收方回電話的人數,最後並用suspecters數組保存全部的嫌疑人。有了以上的容器後,接下來就是獲取全部的嫌疑人了,直接遍歷二維數組G,使用i表示caller,j表示receiver,只要G[i][j]!=0&&G[i][j]<=5
,說明當前caller打了一次短電話,累計++frequency[i]
,在此基礎上,若是G[j][i]!=0
,說明receiver回電話了,累計++callback[i]
,在遍歷完全部caller打的電話記錄後,判斷是否frequency[i]>K&&callback[i]<=floor(0.2*frequency[i])
若是是,說明i是嫌疑人,將其加入到suspecters。blog
使用visited數組標記每個嫌疑人是否被訪問,set<int> gang
保存每個幫派的人(一次DFS保存一個),bool hasGang
標記是否有幫派,初始爲false,而後就使用DFS遍歷整個圖,每一次DFS遍歷一個gang,遍歷以前得清空gang集合的內容,核心代碼以下:排序
void DFS(int root,int K){ visited[root] = true; gang.insert(root); for (int suspecter : suspecters) { if(!visited[suspecter]&&G[root][suspecter]!=0&&G[suspecter][root]!=0){ //訪問每個沒有被訪問而且與當前root相互打電話的嫌疑人 DFS(suspecter,K); } } } bool hasGang = false; for (int suspecter : suspecters) { if(!visited[suspecter]){ gang.clear(); DFS(suspecter,K); int num = 0; for(auto &item:gang){ hasGang = true;// 有gang存在 printf("%d",item); if(num<gang.size()-1) printf(" "); ++num; } printf("\n"); } }
在預處理完成以後,就要將全部屬於一個gang的嫌疑人進行合併,合併操做使用Union函數來實現,因爲嫌疑人是從小到大加入的集合,咱們就能夠直接從第一個沒有被訪問的嫌疑人開始,將他和其同屬於一個gang的其餘嫌疑人一併進行輸出便可,這樣就省去了排序的麻煩,其核心代碼以下:
int father[1005]; void init(){ for(int i=0;i<1005;++i){ father[i] = i; } } int getFather(int a){ while(a!=father[a]){ a = father[a]; } return a; } void Union(int a,int b){ int fa = getFather(a); int fb = getFather(b); if(fa!=fb){ father[fb] = fa; } } // 合併全部的嫌疑人 for(int i:suspecters){ for(int j:suspecters){ if(i!=j&&G[i][j]!=0&&G[j][i]!=0){ // i和j是一個gang Union(i,j); } } } // 嫌疑人是從小到大加入的集合,從小到大直接輸出便可 for(int i=0;i<suspecters.size();++i){ if(!visited[suspecters[i]]){ visited[suspecters[i]] = true; printf("%d",suspecters[i]); for(int j=i+1;j<suspecters.size();++j){ if(!visited[suspecters[j]]&&getFather(suspecters[i])==getFather(suspecters[j])){ visited[suspecters[j]] = true; printf(" %d",suspecters[j]); } } printf("\n"); } }
#include <string> #include <cmath> #include <set> #include <vector> using namespace std; int G[1005][1005]; int frequency[1005];// 統計每個人打電話不超過5分鐘的次數 int callback[1005];// 每個嫌疑人被回電話的人數 vector<int> suspecters;// 全部的嫌疑人 bool visited[1005];// 標記每個嫌疑人是否被訪問 set<int> gang;// 每個幫派的嫌疑人 void DFS(int root,int K){ visited[root] = true; gang.insert(root); for (int suspecter : suspecters) { if(!visited[suspecter]&&G[root][suspecter]!=0&&G[suspecter][root]!=0){ DFS(suspecter,K); } } } int main(){ int K,N,M;//閾值,頂點數目,邊數目 scanf("%d %d %d",&K,&N,&M); for (int i = 0; i < M; ++i) { int a,b,time; scanf("%d %d %d",&a,&b,&time); G[a][b] += time; } for(int i=1;i<=N;++i){ for (int j = 1; j <=N ; ++j) { if(G[i][j]!=0&&G[i][j]<=5){ ++frequency[i]; if(G[j][i]!=0){ // j給i回電話了 ++callback[i]; } } } if(frequency[i]>K&&callback[i]<=floor(0.2*frequency[i])){ // 打不超過5分鐘的電話的次數超過了閾值,而且回電話的人數不超過20% suspecters.push_back(i); } } bool hasGang = false; for (int suspecter : suspecters) { if(!visited[suspecter]){ gang.clear(); DFS(suspecter,K); int num = 0; for(auto &item:gang){ hasGang = true; printf("%d",item); if(num<gang.size()-1) printf(" "); ++num; } printf("\n"); } } if(!hasGang){ printf("None"); } return 0; }
#include<cstdio> #include <cmath> #include <vector> using namespace std; int G[1005][1005]; int frequency[1005];// 統計每個人打電話不超過5分鐘的次數 int callback[1005];// 每個嫌疑人被回電話的人數 vector<int> suspecters;// 全部的嫌疑人 bool visited[1005];// 標記每個嫌疑人是否被訪問 int father[1005]; void init(){ for(int i=0;i<1005;++i){ father[i] = i; } } int getFather(int a){ while(a!=father[a]){ a = father[a]; } return a; } void Union(int a,int b){ int fa = getFather(a); int fb = getFather(b); if(fa!=fb){ father[fb] = fa; } } int main(){ init(); int K,N,M;//閾值,頂點數目,邊數目 scanf("%d %d %d",&K,&N,&M); for (int i = 0; i < M; ++i) { int a,b,time; scanf("%d %d %d",&a,&b,&time); G[a][b] += time; } for(int i=1;i<=N;++i){ for (int j = 1; j <=N ; ++j) { if(G[i][j]!=0&&G[i][j]<=5){ ++frequency[i]; if(G[j][i]!=0){ // j給i回電話了 ++callback[i]; } } } if(frequency[i]>K&&callback[i]<=floor(0.2*frequency[i])){ // 打不超過5分鐘的電話的次數超過了閾值,而且回電話的人數不超過20% suspecters.push_back(i); } } // 合併全部的嫌疑人 for(int i:suspecters){ for(int j:suspecters){ if(i!=j&&G[i][j]!=0&&G[j][i]!=0){ // i和j是一個gang Union(i,j); } } } // 嫌疑人是從小到大加入的集合,從小到大直接輸出便可 for(int i=0;i<suspecters.size();++i){ if(!visited[suspecters[i]]){ visited[suspecters[i]] = true; printf("%d",suspecters[i]); for(int j=i+1;j<suspecters.size();++j){ if(!visited[suspecters[j]]&&getFather(suspecters[i])==getFather(suspecters[j])){ visited[suspecters[j]] = true; printf(" %d",suspecters[j]); } } printf("\n"); } } if(suspecters.empty()){ printf("None"); } return 0; }