题目链接:
https://vjudge.net/problem/1735275/origin
基本思路:
本题思路比较简单,首先,我们知道 a & x = b, b & x = b; 所以,一个数通过与运算改变只能改变一次!
所以,这里就有一种暴力的写法,三次for循环,时间复杂度是O(3n)。
第一次,从1到n遍历一遍,计算各个元素出现的次数,如果大于等于2,则输出0,结束。
第二次,从1到n遍历,如果存在 vis[ a[i]&k ] >= 2 并且 与k与运算后的值与之前的不同,即a[i] != a[i]&k,于是输出1,结束。
第三次,从1到n遍历,对a[i]&k的元素加一,即vis[ a[i]&k ]++,如果存在vis[ a[i]&k ] >= 2 && (a[i]&k) != a[i],则输出2,结束。
AC代码如下:
#include <iostream> #include <cstdio> using namespace std; const int MX = 1e5+10; int a[MX], vis[MX]; int main() { int n, k; scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 1; i <= n; ++i) { vis[ a[i] ]++; // 第一次统计一下元素的个数 if(vis[ a[i] ] >= 2) { printf("0 "); return 0; } } for(int i = 1; i <= n; ++i) { // 一次与运算有值并且这个与运算得到的结果和之前的值不同时则说明操作一次即可 if(vis[ a[i]&k ] == 1 && a[i] != (a[i]&k)) // 注意要加括号! { printf("1 "); return 0; } } for(int i = 1; i <= n; ++i) { vis[ a[i]&k ]++; // 操作两次时直接遍历一遍 if(vis[ a[i]&k ] >= 2 && a[i] != (a[i]&k)) // 这里要注意一下与运算重复的现象! { printf("2 "); return 0; } } printf("-1 "); return 0; }
第二种解法用到了flag标记。
第一种情况,未进行与运算就存在,则输出0。
第二种情况, 进行了一次与运算与之前元素重复,或者输入元素与之前与运算元素重复,则与对比1求最小值。
第三种情况, 与运算结果与之前与运算结果相同则与2对比得最小值。
下面是AC代码:
#include <iostream> #include <cstdio> using namespace std; const int INF = 0x3f3f3f3f; const int MX = 1e5+10; bool flag[MX][3]; // 一个数有两种状态! int main() { int ans = INF; int n, k; scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) { int x; scanf("%d", &x); int y = x&k; if(flag[x][0]) // 未进行与运算就存在,则输出0 { ans = min(ans, 0); } else if(flag[y][0] || flag[x][1]) //进行了一次与运算与之前元素重复,或者输入元素与之前与运算元素重复,则与对比1求最小值 { ans = min(ans, 1); } else if(flag[y][1]) // 与运算结果与之前与运算结果相同则与2对比得最小值 { ans = min(ans, 2); } flag[x][0] = true; flag[y][1] = true; } if(ans == INF) printf("-1 "); // 都不符合作则输出-1 else printf("%d ", ans); return 0; }
如有疑问,欢迎评论指出!