4305 不重复数字

博客内容讲述了如何利用哈希表解决C++编程中去除重复数字的问题。通过创建一个哈希表,对输入的数字进行检查,若不在哈希表内则添加并输出,从而实现不重复输出的功能。代码示例展示了具体实现过程,包括初始化哈希表、读取数据、处理每个数字并检查是否存在。

4305 不重复数字

啊,就是将数字去重,我在想,如果去重函数会不会…
结果,我…还是乖乖的去打hash了
这个题就是一个模板hash题,我好开心
1.开一个hash表,用来存hash值
2.t组数据,处理每一组数据都需要清空hash表
3.对于每一个数据,一次读入n个数,每一次对一个数进行一个处理,如果存在hash表中,那么不用管了,不存在的话,就将它加入hash表中,然后将其输出就行
好简单
但是hash表的初始值需要赋值为一个乱七八杂的数就行

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define hmod 4698571    //mod 
inline ll read();
ll Hash[hmod+123456];
inline bool find(ll x)
{
	ll wh=abs(x%hmod);   //表示位置
	while (Hash[wh]!=1808504320951916825)//如果当前位置有数
	{
		if (Hash[wh]==x) //查看是否为此数
			return true; //返回true
		else
			++wh;      //查找下一个位置
	}
	Hash[wh]=x;       //没找到则放入Hash中
	return false;
}

ll t,n;
inline ll read()//快读好省时间
{
	ll x=0,f=0;
	char c=getchar();
	while (!isdigit(c))
		f|=c=='-',c=getchar();
	while (isdigit(c))
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f?-x:x;
}
int main()
{
	t=read();
	while (t--)
	{
		memset(Hash,25,sizeof(Hash));//乱七八糟的值
		n=read();
		for (register int i=1;i<=n;++i)
		{
			ll k=read();
			if (!find(k))//不在hash表中
				cout<<k<<" ";//输出
		}
		cout<<endl;
	}
	return 0;
}
<think>嗯,用户问的是如何用C++解决洛谷的P4305题目。我需要先回忆一下这道题的具体内容。P4305的题目名称是“[JLOI2011]重复数字”,对吧?这道题的要求应该是给定一组数,输出其中重复出现的数字,保持它们在原序列中的第一次出现顺序。也就是说,去重但保留顺序,而是简单地使用集合或者排序处理。 首先,用户可能需要了解如何高效地处理这个问题。因为输入的数据量可能比较大,所以普通的暴力方法可能会超时。比如,对于每个元素遍历之前的所有元素检查是否重复,这样的时间复杂度是O(n²),对于大数据量肯定行。所以需要考虑更高效的方法,比如使用哈希表来记录已经出现过的元素,这样每次查找的时间是O(1)。 接下来,用户可能需要具体的实现步骤。比如,如何读取输入,如何处理多组测试用例。题目通常会有多组测试数据,每组数据包含一个数字n,然后是n个整数。所以程序需要先读取测试用例的数量,然后循环处理每组数据。 然后,关于数据结构的选择。C++中的unordered_map或者unordered_set可以用来快速判断元素是否已经存在。但需要注意的是,unordered_set在插入和查询上的平均时间复杂度是O(1),而遍历的时候需要保持元素的插入顺序。但这里需要保留的是原序列中的第一次出现的顺序,所以应该按照原顺序遍历每个元素,将未出现过的元素加入结果列表,并标记为已出现。 具体的步骤可能是这样的:对于每组测试数据,创建一个空的vector或者数组来保存结果,同时创建一个unordered_set来保存已经出现的元素。然后遍历输入的每个数字,如果该数字在unordered_set中,就将其加入结果vector,并插入到unordered_set中;否则跳过。最后输出结果vector中的元素,用空格分隔。 需要注意的是,输入输出的效率问题。因为数据量可能很大,使用cin和cout可能会比较慢,这时候可以考虑使用scanf和printf,或者关闭同步流来加速输入输出。比如在C++中使用ios::sync_with_stdio(false); cin.tie(0);这样可以提高速度。 另外,关于测试数据中的数字范围,题目中的数字可能比较大,比如绝对值超过10^9,所以用int可能足够,但要看具体的情况。过C++中的int通常是32位,可以处理到2e9左右,所以应该没问题。但如果是更大的数可能需要用long long,但题目里应该会有这种情况。 可能的错误点包括:处理多个测试用例时,没有清空之前的集合和结果数组,导致同测试用例之间的数据混淆。所以在每个测试用例处理前,必须清空结果vector和unordered_set。 还有一个点,输出的最后一个数字后面能有空格,否则可能导致格式错误。所以在输出的时候需要控制,比如先输出第一个元素,然后后面的元素前面加空格,或者用join函数来拼接字符串。过在C++中,可以逐个输出元素,第一个之后每个前面加空格,或者在循环中处理。 另外,关于输入的方式,因为每组测试用例的n之后有n个数字,所以需要正确读取每个数字。需要注意的是,有时候输入中可能存在多个空格或者换行符分隔,但C++的cin会自动处理这些,所以用循环读取n次即可。 可能的测试用例是,比如输入: 3 1 2 2 输出应该是1 2。或者输入: 5 1 2 3 2 1,输出是1 2 3。 总结一下,解决步骤是: 1. 读取测试用例数T。 2. 对于每个测试用例: a. 读取n。 b. 读取接下来的n个数字,保存到一个临时数组或直接处理。 c. 遍历这些数字,使用unordered_set判断是否已经存在,若存在则加入结果vector,并插入到集合中。 3. 输出结果vector中的元素,注意格式。 在代码实现中,需要注意输入输出效率,使用快速的方法。此外,还要处理每组测试用例的初始化,避免数据残留。 例如,代码的大致结构可能是: #include <iostream> #include <unordered_set> #include <vector> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; } unordered_set<int> seen; vector<int> res; for (int num : nums) { if (seen.find(num) == seen.end()) { res.push_back(num); seen.insert(num); } } for (int i = 0; i < res.size(); ++i) { if (i > 0) cout << ' '; cout << res[i]; } cout << '\n'; } return 0; } 这样应该就能正确处理了。但需要测试一下边界情况,比如n=0?过题目中的n应该至少是1。另外,当所有元素都重复时,结果是否正确。比如输入全是2,则输出一个2。 可能还需要注意的是,unordered_set在某些情况下可能会有哈希冲突,导致性能下降,但对于题目中的时间限制来说,一般情况下这种方法应该可以通过。如果遇到更严格的时间限制,可能需要用更快的哈希结构,或者改为使用数组或位图,但题目中的数字范围很大,所以只能用哈希表。 总之,核心思路就是用哈希表来记录已出现的元素,保持顺序的同时去重。</think>下面分步讲解如何使用C++解决洛谷P4305 "[JLOI2011]重复数字": ### 题目分析 给定$n$个整数,保留每个数字**第一次出现的顺序**并去重。例如输入`1 2 2 3 3 1`,输出`1 2 3`。 ### 核心思路 1. **哈希表判重**:用`unordered_set`记录已出现的数字 2. **顺序保留**:遍历原始数组时,按顺序收集首次出现的数字 3. **输入输出优化**:关闭同步流加速IO ### 代码实现 ```cpp #include <iostream> #include <unordered_set> #include <vector> using namespace std; int main() { // 优化输入输出(关键步骤) ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; // 读取测试用例数量 while (T--) { int n; cin >> n; // 当前用例的数字个数 vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; // 读取所有数字 } unordered_set<int> seen; // 哈希表判重 vector<int> result; // 保存结果 for (int num : nums) { if (!seen.count(num)) { // 首次出现 result.push_back(num); seen.insert(num); } } // 格式化输出结果 for (size_t i = 0; i < result.size(); ++i) { if (i > 0) cout << " "; // 数字间用空格分隔 cout << result[i]; } cout << "\n"; // 换行符结束当前用例 } return 0; } ``` ### 关键点说明 1. **复杂度控制**: - 时间复杂度:$O(n)$,每个元素仅被处理一次 - 空间复杂度:$O(n)$,哈希表和结果数组各存储n个元素 2. **输入输出优化**: ```cpp ios::sync_with_stdio(false); // 解除C与C++IO同步 cin.tie(nullptr); // 解除cin与cout的绑定 ``` 这两行代码可使IO速度提升约10倍,有效防止大数据超时 3. **格式处理技巧**: - 输出时第一个元素前加空格 - 后续元素前添加空格 - 行末用换行符`\n`代替`endl`避免频繁刷新缓冲区 ### 测试用例演示 输入: ``` 3 11 1 2 3 4 5 6 7 8 9 10 1 5 1 1 1 1 1 6 1 2 3 2 3 4 ``` 输出: ``` 1 2 3 4 5 6 7 8 9 10 1 1 2 3 4 ``` ### 易错点提醒 - 忘记处理多组测试数据 - 输出格式末尾多空格 - 未关闭同步流导致IO超时 - 未及时清空哈希表和结果数组(本代码通过局部变量自动处理)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值