嵊州D5T3 指令 program 神奇的位运算
指令 program
【问题描述】
krydom 有一个神奇的机器。
一开始,可以往机器里输入若干条指令: opt x 其中,opt 是 & | ^ 中的一种,0 ≤ x ≤ 1023 。
对于 0 到 1023 的每一个数 m,机器会输出 m 按照指令依次运算后的结果。
现在,krydom 往里面输入了 n 条指令,但是 zcysky 觉得 krydom naive, 只用 3 条指令就实现了和这 n 条指令一样的功能。
由于 krydom 很菜,希望你们帮帮他,但是只需要输出 5 条以内的指令就行了。
【输入格式】
第一行包括一个整数 n,表示指令的个数。 接下来 n 行,每行一个字符 opt 和一个数字 x,表示 ans = ans opt x
【输出格式】
第一行输出一个整数 m,表示有 m 条新指令。(要求 m ≤ 5)
接下来 m 行,每行一个字符 opt 和一个数字 x。表示一条新指令。
【输入输出样例】
Input1 | Input2 | Input3 |
3 | 3 ^ 2 | 1 |
3 &1 & 3 & 5 |
3 ^ 1 ^ 2 ^ 3 |
Output1 | Output2 | Output3 |
2 | 3 ^ 2 |
1 & 1 |
0 |
【样例解释】
样例 2 解释:
((x&1)&3)&5 = x&(1&3&5) = x&1
样例 3 解释:
((x^1)^2)^3 = x^(1^3^4) = x^0 = x
【数据范围】
对于 20% 的数据满足: n ≤ 5 。
对于 50% 的数据满足: x ≤ 3 。
对于 100%的数据满足: n ≤ 5*10^5
吐槽
这是道使人有很多想法的题目呢
当我看到位运算是,我想到的是<bitset>……------------STL大法好!
后来,发现不需要那些高级函数和类方法,反倒觉得是搜索
因为我只要从一个数出发,能通过三种运算,得到最终结果即可
DFS
#include<bits/stdc++.h> using namespace std; char opt1,opt2[6]; int first=1023; int num2[6],ch=first,flag=0;//ch:目标状态 void dfs(int nowch,int depth){//nowch:dfs现在状态 if(depth>5) return;//(要求m ≤ 5) if(flag==1) return; //是否成功 if(ch==nowch) { cout<<depth<<endl; for(int i=0;i<depth;i++) cout<<opt2[i]<<" "<<num2[i]<<endl; flag=1; return; } //三种方法 for(int i=1;i<=1023;i++){ num2[depth]=i; opt2[depth]='&'; dfs(nowch&i,depth+1); opt2[depth]='|'; dfs(nowch|i,depth+1); opt2[depth]='^'; dfs(nowch^i,depth+1); num2[depth]=0; } return; } int main(){ // freopen("program.in","r",stdin); // freopen("program.out","w",stdout); int n; cin>>n; int num1[n],ans=n; for(int i=0;i<n;i++) { cin>>opt1; cin>>num1[i]; if(opt1=='&') ch&=num1[i]; if(opt1=='|') ch|=num1[i]; if(opt1=='^') ch^=num1[i]; } dfs(first,0); return 0; }
后来发现,DFS可以是可以,但是那个first不能仅仅是一个数。
否则,除了一些特殊解,得到的答案可能是错的(非常错!错到离谱!)
因为没有人知道,那台机器到底是按哪个数作为标准的
何况,它会把1到1023(1111111111)全输出
按道理说吧,那基础的几十分总是能拿到的吧?
但是,dfs最大的敌人,就是还没搜到就超时了。。。
于是,我们就惊讶的看到了std。。。
std
1 #include <cstdio> 2 #define FOR(i, l, r) for(int i = l; i <= r; ++i) 3 using namespace std; 4 int n, x, y, opt, f[11]; 5 char s[5]; 6 //用来表示 and& or| xor^ 7 int work(int now, int opt) 8 { 9 if (opt == 1) return 0;//如果是&,返回0 10 if (opt == 2) return 1;//如果是|,返回1 11 if (now <= 1) return now ^ 1;//如果now小于等于1,返回now对1按位异或 12 else return 5 - now;//now大于1,返回0101-now 13 } 14 int output(int x) 15 { 16 int ans = 0;//每次输出结果先归0 17 for(int i = 10; i; --i) 18 { 19 ans <<= 1;//每次把ans左移一位,差不多相当于是自乘2 (0左移一位(或者说是*2)还是0哟) 20 if (f[i] == x) ans |= 1;//如果f[10~1]等于x(输进来的数),就把ans按位或于1,即把最低位变成1 21 } 22 return ans; 23 } 24 int main() 25 { 26 // freopen("program.in", "r", stdin); 27 // freopen("program.out", "w", stdout); 28 29 FOR(i, 1, 10) f[i] = 2; 30 scanf("%d", &n); 31 FOR(i, 1, n) 32 { 33 scanf("%s%d", s, &x);//输入 34 if (s[0] == '&') opt = 1; else//分别用1,2,3来存储& | ^ 35 if (s[0] == '|') opt = 2; else opt = 3; 36 FOR(j, 1, 10) 37 { 38 y = x & 1;//把x与1按位与的值赋给y 39 if (opt == 1 && !y) f[j] = work(f[j], 1); 40 if (opt != 1 && y) f[j] = work(f[j], opt); 41 x >>= 1; 42 } 43 } 44 puts("3");//固定输出3,方便与三个符号都有操作 45 printf("& %d\n", output(0) ^ 1023);//把结果与1111111111取按位异或 46 printf("| %d\n", output(1)); 47 printf("^ %d\n", output(3)); 48 //其实只要m<=5就好了,但是这是最简单的方法 49 return 0; 50 }
std的解析都在注释里了。
因为这道题是SPJ,所以就算输出后面跟着的是0也行,不输出也行。
这种方法也告诉我们,多种位运算是可以用三种位运算表示滴~
有木有感觉很神奇?