题目

集装箱运输货物时,我们必须特别小心,不能把不相容的货物装在一只箱子里。比如氧化剂绝对不能跟易燃液体同箱,否则很容易造成爆炸。

本题给定一张不相容物品的清单,需要你检查每一张集装箱货品清单,判断它们是否能装在同一只箱子里。

输入格式:
输入第一行给出两个正整数:N (≤10 <math> <semantics> <mrow> <msup> <mn> 4 </mn> </msup> </mrow> <annotation encoding="application&#47;x&#45;tex"> ^4 </annotation> </semantics> </math>4) 是成对的不相容物品的对数;M (≤100) 是集装箱货品清单的单数。

随后数据分两大块给出。第一块有 N 行,每行给出一对不相容的物品。第二块有 M 行,每行给出一箱货物的清单,格式如下:

K G[1] G[2] … G[K]

其中 K (≤1000) 是物品件数,G[i] 是物品的编号。简单起见,每件物品用一个 5 位数的编号代表。两个数字之间用空格分隔。

输出格式:
对每箱货物清单,判断是否可以安全运输。如果没有不相容物品,则在一行中输出 Yes,否则输出 No。

输入样例:

6 3
20001 20002
20003 20004
20005 20006
20003 20001
20005 20004
20004 20006
4 00001 20004 00002 20003
5 98823 20002 20003 20006 10010
3 12345 67890 23333

输出样例:

No
Yes
Yes

分析

题解一

暴力法求解,把每一个清单里的数和不相容物品第一个数进行对比,如果相等,再把每一个清单里的数和不相容物品第二个数进行对比,如果都一致,则说明清单不安全。

#include<iostream>
using namespace std;
int main(){
	int cp[10000+5][2];
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){  //存储每对不相容物品
		cin>>cp[i][0]>>cp[i][1];
	}
	for(int i=0;i<m;i++){
		int k;
		int list[1000+5];
		bool flag=true;
		cin>>k;
		for(int j=0;j<k;j++){  // 存储每张清单
			cin>>list[j];
		}
		for(int j=0;j<n;j++){  // 遍历每个不相容物品的第一个数
			for(int ii=0;ii<k;ii++){    // 遍历清单中每个数
				if(cp[j][0]==list[ii]){  // 如果第一个数相当,遍历查找是否第二个数也相等
					for(int jj=0;jj<k;jj++){
						if(cp[j][1]==list[jj])    // 第一、第二个数都相等,说明清单不安全
							flag=false;
					}
				}
			}
		}
		cout<<((flag)?"Yes":"No")<<endl;
	}
	return 0;
}

时间复杂度为 <math> <semantics> <mrow> <mi> O </mi> <mo> ( </mo> <mi> m </mi> <mi> n </mi> <msup> <mi> k </mi> <mn> 2 </mn> </msup> <mo> ) </mo> </mrow> <annotation encoding="application&#47;x&#45;tex"> O(mnk^2) </annotation> </semantics> </math>O(mnk2),所以毫不意外的最后两个测试点超时了

算法二

考虑优化算法,既然每次如果第一个数在清单中出现,都要再次遍历清单查找第二个数,那我们可以把每次第一个数出现的序号和第二个数出现的序号分别存起来,然后再遍历存起来的俩数组,看是否有相同序号,如果有,则说明出现不相容物品,反之。

#include<iostream>
using namespace std;
int main(){
	int cp[10000+5][2];
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>cp[i][0]>>cp[i][1];
	}
	for(int i=0;i<m;i++){
		int k;
		int list[1000+5];
		int cp_sure[1000+5][2];   //存储在清单中出现过的不相容物品
		int cp_1=0,cp_2=0;    // cp_1 是清单中出现的第一列的不相容物品,cp_2 同理
		bool flag=true;
		cin>>k;
		for(int j=0;j<k;j++){
			cin>>list[j];
		}
		for(int j=0;j<n;j++){
			for(int ii=0;ii<k;ii++){
				if(cp[j][0]==list[ii]){   // 如果第一列不相容物品在清单中出现
					cp_sure[cp_1][0] = j;   //存进 cp_sure 数组第一列中
					cp_1++;
				}else if(cp[j][1]==list[ii]){  // 如果第二列不相容物品在清单中出现
					cp_sure[cp_2][1] = j;  //存进 cp_sure 数组第二列中
					cp_2++;
				}
			}
		}
		// 再判断俩数组,如果序号相等则说明是一对不相容物品
		for(int j=0;j<cp_1;j++){
			for(int ii=0;ii<cp_2;ii++){
				if(cp_sure[j][0]==cp_sure[ii][1])
					flag=false;
			}
		}
		cout<<((flag)?"Yes":"No")<<endl;
	}
	return 0;
}

以空间换时间,时间复杂度优化到 <math> <semantics> <mrow> <mi> O </mi> <mo> ( </mo> <mi> m </mi> <mi> n </mi> <mi> k </mi> <mo> ) </mo> </mrow> <annotation encoding="application&#47;x&#45;tex"> O(mnk) </annotation> </semantics> </math>O(mnk),依然最后两个测试点通不过(orz)

算法三

我们继续优化算法,这次换个方向,既然清单中的值唯一序号无关,而我们又需要方便查找清单中的值,那我们换存储清单的结构,用 set 集合,set 中有 find 函数(如果找到值返回该值的迭代器,否则返回 end 迭代器),时间复杂度为 <math> <semantics> <mrow> <mi> O </mi> <mo> ( </mo> <mi> l </mi> <mi> o </mi> <mi> g </mi> <mi> N </mi> <mo> ) </mo> </mrow> <annotation encoding="application&#47;x&#45;tex"> O(logN) </annotation> </semantics> </math>O(logN)

#include<iostream>
#include<set>
using namespace std;
#define MAX 10000+5
int main(){
	int cp[MAX][2];
	int n,m;
	set<int> s;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>cp[i][0]>>cp[i][1];
	}
	for(int i=0;i<m;i++){
		int k;
		bool flag=true;
		cin>>k;
		for(int j=0;j<k;j++){
			int t;
			cin>>t;
			s.insert(t);
		}
		for(int j=0;j<n;j++){
			if(s.find(cp[j][0])!=s.end() && s.find(cp[j][1])!=s.end())  //在清单集合中找到不相容的一组物品
				flag=false;
		}
		cout<<((flag)?"Yes":"No")<<endl;
		s.clear();
	}
	return 0;
}

不容易啊,终于过了,时间复杂度为 <math> <semantics> <mrow> <mi> O </mi> <mo> ( </mo> <mi> m </mi> <mi> n </mi> <mi> l </mi> <mi> o </mi> <mi> g </mi> <mi> k </mi> <mo> ) </mo> </mrow> <annotation encoding="application&#47;x&#45;tex"> O(mnlogk) </annotation> </semantics> </math>O(mnlogk)