《孟子 · 告子上》有名言:“鱼,我所欲也,熊掌,亦我所欲也;二者不可得兼,舍鱼而取熊掌者也。”但这世界上还是有一些人可以做到鱼与熊掌兼得的。
给定 n 个人对 m 种物品的拥有关系。对其中任意一对物品种类(例如“鱼与熊掌”),请你统计有多少人能够兼得?
输入格式:
输入首先在第一行给出 2 个正整数,分别是:n(≤105)为总人数(所有人从 1 到 n 编号)、m(2≤m≤105)为物品种类的总数(所有物品种类从 1 到 m 编号)。
随后 n 行,第 i 行(1≤i≤n)给出编号为 i 的人所拥有的物品种类清单,格式为:
K M[1] M[2] ... M[K]
其中 K
(≤103)是该人拥有的物品种类数量,后面的 M[*]
是物品种类的编号。题目保证每个人的物品种类清单中都没有重复给出的种类。
最后是查询信息:首先在一行中给出查询总量 Q(≤100),随后 Q 行,每行给出一对物品种类编号,其间以空格分隔。题目保证物品种类编号都是合法存在的。
输出格式:
对每一次查询,在一行中输出两种物品兼得的人数。
思路1:
存储每个人拥有的物品种类数量,查询的时候判断每一个人是否两个物品都有,时间复杂度是1e10。
代码1:
#include <iostream>
#include <algorithm>
#include <vector>
#define N 100010
using namespace std;
int n,m,k,x,y,q;
vector<int> v[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>k;
for(int j=1;j<=k;j++){
cin>>x;
v[i].push_back(x);//每个人拥有的物品编号
}
sort(v[i].begin(),v[i].end());//排序,方便查找
}
cin>>q;
for(int qq=1;qq<=q;qq++){
int ans=0;
cin>>x>>y;
if(x>y) swap(x,y);//先找编号小的
for(int i=1;i<=n;i++){
int p=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();//第一个大于等于x的
if(p!=-1&&v[i][p]==x){
p=lower_bound(v[i].begin()+p,v[i].end(),y)-v[i].begin();//从x的位置往后找y
if(p!=-1&&v[i][p]==y) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
思路2:
存储每一个物品被哪些人拥有,查询的时候对两个集合做交集,求交集的大小,时间复杂度为1e7。
代码2:
#include <iostream>
#include <algorithm>
#include <vector>
#define N 100010
using namespace std;
int n,m,k,x,y,q;
vector<int> v[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>k;
for(int j=1;j<=k;j++){
cin>>x;
v[x].push_back(i);
}
}
cin>>q;
for(int qq=1;qq<=q;qq++){
vector<int> ans;
cin>>x>>y;
set_intersection(v[x].begin(),v[x].end(),v[y].begin(),v[y].end(),inserter(ans,ans.begin()));
cout<<ans.size()<<endl;
}
return 0;
}