Balloon | ||
[ Submit Code ] [ Top 20 Runs ] [ Runs Status ] | ||
Acceteped : 706 | Submit : 2163 | |
Time Limit : 1000 MS | Memory Limit : 65536 KB | |
Description | ||
题目描述 ICPC比赛当你过了一道题以后,会发一个标识这道题颜色的气球。 现给你2个队过的气球的颜色,求他们都过了的气球是哪些? 输入 第一行是一个整数K,表示样例的个数(K≤100)。 每个样例的第一行是两个整数N和M,1≤N,M≤100000,表示两个队分别过题的数量; 第二、三行分别是N和M个有序的整数Xi和Yi,0≤Xi,Yi≤10^9,表示气球的颜色。 输入数据保证集合{Xi}和{Yi}的元素都是唯一的,且两个集合一定存在交集。 输出 每个样例输出两行,第一行输出相同的过题数目S,第二行按升序输出S个整数,每个整数之间空一个空格。 样例输入 2 3 3 1 2 3 1 2 3 3 2 1 2 3 2 3 样例输出 3 1 2 3 2 2 3 |
方法1 -- 离散化
思路
首先看数据范围【0≤Xi,Yi≤10^9】,所以想要用一个vis数组,把Xi、Yi作为下标来判断这个值是否出现过不可行,数组明显开不了那么大。
可以看到:虽然【0≤Xi,Yi≤10^9】,但是数组的长度就小了几个数量级【1≤N,M≤100000】,这时候就可以利用离散化,把较大的值域区间压缩到较小的定义域区间内
建立起了这种映射关系后,想要判断一个数是否出现过,那就只要判断它在较小区间内对应的值是否出现过即可,本题中的小区间【1≤N,M≤100000】,这个大小就可以借助数组来判断了
那么怎么建立这种映射关系呢,最简单的就是把【目标数值】映射到【这个数值在整个数组中排第几大】,然后这个时候就可以用到二分查找了。
实现
- 将两个数组合并、排序,然后去重,得到一个新数组set;
- 对X数组中的所有值在set中进行二分查找,得到的下标idx就是这个值排第几大,建立一个vis数组,令vis[idx]=1;
- 这时候X数组中的值都标记过了,然后再对Y数组中的值也进行二分查找,看得到的下标在vis中是否被标记过,标记过就说明这个值也在X中,直接加入答案中。
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100005
int X[N] = {0};
int Y[N] = {0};
int set[N * 2] = {0};
int cmp(const void* a, const void* b) {
return *((int *)a) - *((int *)b);
}
int binary_search(int x, int left, int right) {
while (left <= right) {
int mid = left + (right - left) / 2;
if (set[mid] > x) {
right = mid - 1;
} else if (set[mid] < x) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int m, n;
int len = 0, idx = 0;
scanf("%d %d", &m, &n);
for (int i = 0; i < m; i++) {
scanf("%d", &X[i]);
set[idx++] = X[i];
}
for (int i = 0; i < n; i++) {
scanf("%d", &Y[i]);
set[idx++] = Y[i];
}
// 排序、去重
qsort(set, idx, sizeof(int), cmp);
for (int i = 0; i < idx; i++) {
if (i == 0 || set[i] != set[i - 1]) {
set[len++] = set[i];
}
}
int cnt = 0;
int ans[m + n] = {0};
int vis[m + n] = {0};
for (int i = 0; i < m; i++) {
// 离散化,通过二分查找把较大的数据压缩到一个较小的区间内,就可以当成数组下标了
vis[binary_search(X[i], 0, len - 1)] = 1;
}
for (int i = 0; i < n; i++) {
if (vis[binary_search(Y[i], 0, len - 1)] == 1) {
ans[cnt++] = Y[i];
}
}
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++) {
printf("%d", ans[i]);
printf(i == cnt - 1 ? "\n" : " ");
}
}
}
优化
在得到set数组的时候,要对X、Y合并后的数组排序,时间复杂度为,但是注意到在题目中,X、Y数组都是有序的,所以就可以利用以下的方法得到set
贴一个链接,方便理解合并的步骤:如何快速合并两个有序数组? - 知乎 (zhihu.com)
设置两个数 i1=0、i2=0,然后分以下情况讨论:
- X[i1] < Y[i2]: 把X[i1]填入set数组中,i1++
- X[i1] > Y[i2]: 把Y[i2]填入set数组中,i2++
- X[i1] == Y[i2]: 任选一个填入set数组,i1、i2同时加一
- 若是i1等于数组X的长度后,将Y数组i2以后的值依次填入set,反之将X数组剩余的数填入
这样就充分利用了题目所给的条件,时间复杂度为,代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100005
int X[N] = {0};
int Y[N] = {0};
int set[N * 2] = {0};
int binary_search(int x, int left, int right) {
while (left <= right) {
int mid = left + (right - left) / 2;
if (set[mid] > x) {
right = mid - 1;
} else if (set[mid] < x) {
left = mid + 1;
} else {
return mid;
}
}
return 0;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int m, n;
int len = 0, idx = 0;
scanf("%d %d", &m, &n);
for (int i = 0; i < m; i++) {
scanf("%d", &X[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &Y[i]);
}
int i1 = 0, i2 = 0;
while (i1 < m && i2 < n) {
if (X[i1] < Y[i2]) {
set[idx++] = X[i1];
i1++;
} else if (X[i1] > Y[i2]) {
set[idx++] = Y[i2];
i2++;
} else {
set[idx++] = X[i1]; // 或Y[i2];
i1++, i2++;
}
}
while (i1 < m) {
set[idx++] = X[i1++];
}
while (i2 < n) {
set[idx++] = Y[i2++];
}
int cnt = 0;
int ans[m + n] = {0};
int vis[m + n] = {0};
for (int i = 0; i < m; i++) {
// 离散化,通过二分查找把较大的数据压缩到一个较小的区间内,就可以当成数组下标了
vis[binary_search(X[i], 0, idx - 1)] = 1;
}
for (int i = 0; i < n; i++) {
if (vis[binary_search(Y[i], 0, idx - 1)] == 1) {
ans[cnt++] = Y[i];
}
}
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++) {
printf("%d", ans[i]);
printf(i == cnt - 1 ? "\n" : " ");
}
}
}
方法2 -- 直接利用双指针
其实在上一种做法的优化中,可以看出因为两个数组有序,在不断地比较X[i1]和Y[i2]这个过程中,就可以直接找出在两个数组中都存在的数,也就是在X[i1] == Y[i2]的时候,这时直接把X[i1]或Y[i2]加入到答案中就行了
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100005
int X[N] = {0};
int Y[N] = {0};
int main() {
int T;
scanf("%d", &T);
while (T--) {
int m, n;
scanf("%d %d", &m, &n);
for (int i = 0; i < m; i++) {
scanf("%d", &X[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &Y[i]);
}
int i1 = 0, i2 = 0;
int cnt = 0, ans[m + n] = {0};
while (i1 < m && i2 < n) {
if (X[i1] < Y[i2]) {
i1++;
} else if (X[i1] > Y[i2]) {
i2++;
} else {
ans[cnt++] = X[i1];
i1++, i2++;
}
}
printf("%d\n", cnt);
for (int i = 0; i < cnt; i++) {
printf("%d", ans[i]);
printf(i == cnt - 1 ? "\n" : " ");
}
}
}