19春第三题 PAT甲级 1158 Telefraud Detection (25分)

34 篇文章 0 订阅
5 篇文章 0 订阅

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.

Input Specification:
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.

Output Specification:
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.

If no one is detected, output None instead.

Sample Input 1:

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
Sample Output 1:

3 5
6

想全部自己写的,还是借鉴了一下
考的主要是图+并查集吧
从1到n遍历一个人,遍历它下面的所有通话记录,如果有通话时长<5的,就将st++,再检测这个接电话的人和打电话的人是否有记录,如果有就将bc++。最后检测st/5和bc哪个大,如果st/5 >=bc,也就是不到五分之一的人回电话了,且st>k,这个人就是坏人,在坏人数组中将它设为true
再次遍历所有人,如果当前这个人是坏人,就再次遍历所有人,如果有人也是坏人,且和它有双向通话(只要检测G[i][j]是否为INF即可),那么就将这两个人合并,因为优先输出集群里小的,所以将大的合并到小的下面。
最后弄一个二维数组存不同集群的坏人,遍历所有人,如果这个人是坏人,就将它存到它集群根结点的数组下面。
最后每次输出集群之前,记得将这个集群排下序再输出

//嫌疑犯:1.每天和不同的k个以上的人打打不超过五分钟的电话
//2.小于等于0.2的人回电话
//3. 两个嫌疑犯互相打电话,就归为同一个集群
//在每一行输出集群里的人按照递增顺序;每一行也按照头目的递增顺序输出
//将小的合并为父亲,最后用一个vector来存集群下的人
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1005;
const int INF = 1000000000;
int father[maxn];
int G[maxn][maxn];
bool isBad[maxn] = {false};
vector<int> isRoot[maxn];

int k,n,m;
void init(){
	for(int i = 1; i <= n; i++){
		father[i] = i;
	}
}

int findFather(int v){
	int a = v;
	while(v != father[v]){
		v = father[v];
	}
	while(a != father[a]){
		int z = a;
		a = father[a];
		father[z] = v;
	}
	return v;
}

void Union(int a,int b){
	int faA = findFather(a);
	int faB = findFather(b);
	if(faA != faB){
		if(faA < faB) father[faB] = faA;
		else father[faA] = faB;
	}
}

int main(){
	fill(G[0], G[0] + maxn * maxn, INF);
	cin>>k>>n>>m;
	init();
	int u,v,time;
	//记录每个人每天的short通话次数,注意是不同人
	//记录每个人被回电话的数量,是哪些短时间通话的人回电话的数量 
	for(int i = 0; i < m; i++){
		cin>>u>>v>>time;
		G[u][v] = time;//这相当于有向图
	}
	for(int i = 1; i <= n; i++){
		int st = 0, bc = 0;
		for(int j = 1; j <= n; j++){
			if(G[i][j] <= 5){
				st++;
				if(G[j][i]!=INF) bc++;
			}
		}
		if((st/5) >= bc && st>k){
			isBad[i] = true;
		}
	}
	for(int i = 1; i <= n; i++){
		if(isBad[i] == true){
			for(int j = 1; j<=n; j++){
				if(isBad[j] == true && G[i][j] != INF && G[j][i]!=INF){
					Union(i,j);
				}
			}
		}
	}
	for(int i = 1; i <= n; i++){
		if(isBad[i] == true) isRoot[findFather(i)].push_back(i);
	}
	bool haveGang = false;
	for(int i = 1; i <= n; i++){
		if(isRoot[i].size() != 0) haveGang = true;
	}
	if(haveGang == false) cout<<"None";
	else{
		for(int i = 1; i <= n; i++){
			if(isRoot[i].size()!=0){
				sort(isRoot[i].begin(),isRoot[i].end());
				for(int j = 0; j < isRoot[i].size(); j++){
					if(j!=0) cout<<" ";
					cout<<isRoot[i][j];
				}
				cout<<endl;
			}
		}
	}
	return 0;
}



用的是并查集,将坏人加入到它的根结点的数组下,还有个dfs,明天看看

//用邻接矩阵来存图,遍历每个人的时候定义st存储小于5分钟的个数,如果对面那个人有回话就将bc++,如果st/5 > bc && st >k,这个人就是坏人
//用并查集来合并每个坏人,将大的合并到小的下面
//如果这个人是坏人,就将它加入到它的根节点下面,最后遍历坏人的二维数组,如果这个人下面有人,就输出它下面的所有坏人 
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1005;
int k,n,m;
int father[maxn];
int G[maxn][maxn];

void init(){
	for(int i = 1; i <= n; i++){
		father[i] = i;
	}
}
int findFather(int v){
	int a = v;
	while(v != father[v]){
		v = father[v];
	}
	while(a != father[a]){
		int z = a;
		a = father[a];
		father[z] = v;
	}
	return v;
}

void Union(int a,int b){
	int faA = findFather(a);
	int faB = findFather(b);
	if(faA < faB) father[faB] = faA;
	else if(faA > faB) father[faA] = faB;
}

int main(){
	cin>>k>>n>>m;
	init();
	int u,v,time;
	vector<int> root[maxn];//root[i]下面的存的是i作为根节点的其他坏人们 
	for(int i = 0; i < m; i++){
		cin>>u>>v>>time;
		//是单向图!
		G[u][v] += time; 
	}
	int bad[maxn] = {0};
	for(int i = 1; i <= n; i++){
		int st = 0,bc = 0;
		for(int j = 1; j <= n; j++){
			if(G[i][j] <= 5 && G[i][j] > 0){
				st++;
				if(G[j][i] != 0){
					bc++;
				}
			}
		}
		if(st/5 >= bc && st > k){
			bad[i] = 1;
		}
	}
	for(int i = 1; i <= n;i++){
		if(bad[i] == 1){
			for(int j = 1; j <= n; j++){
				if(G[i][j]!=0&&G[j][i] != 0 && bad[j] == 1) Union(i,j);
			}
		}
	}
	for(int i = 1; i <= n; i++){
		if(bad[i] == 1){
			root[findFather(i)].push_back(i);
		}
	}
	bool flag = false;
	for(int i = 1; i <= n; i++){
		if(root[i].size()!= 0){
			flag = true;
			for(int j = 0; j < root[i].size(); j++){
				if(j!=0) cout<<" ";
				cout<<root[i][j];
			}
			cout<<endl;
		}
	}
	if(flag == false) cout<<"None\n";
	return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值