有趣的问题
jijidawang · · 个人记录
雾
Problem 1
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
要求:时间复杂度\mathcal O(n) ,不要开数组。
很经典的异或awa
设数组为
异或大家都知道罢:
首先将两个数字转二进制,不足位补
0 。
新数字的一位如果两个数字的那一位相同就为0 否则为1 。a$ 异或 $b$ 记做 $a\oplus b
显然两个相同的数异或后为
当所有数字异或后相同的均消掉肯定只剩下那个唯一的。
#include<iostream>
using namespace std;
int n,tmp,ans; //用 tmp 辗转
int main()
{
cin>>n;
for (int i=0;i<n;i++)
{
cin>>tmp;
ans^=tmp; //在 C/C++ 中按位异或写作 "^"
}
cout<<ans;
return 0;
}
Problem 2
有一个
n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几。
要求:时间复杂度为O(n) ,不要开数组。
这就是两个 Problem 1 的结合。
考虑分治。
因为这两个只出现一次的元素一定是不相同的,所以这两个元素的二进制形式肯定至少有某一位是不同的。
异或后得到这个数字二进制形式中任意一个为
- 将这一位为
0 的所有元素做异或就得出第一个数 - 将这一位为
1 的所有元素做异或就得出另一个数。
Problem 3
判断
n 是否是2 的正整数的幂。
按位与运算:
首先将两个数字转二进制,不足位补
0 。
新数字的一位如果两个数字的那一位都为1 才为1 否则为1=0 。
众所周知
判下除 fa 即可。
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
if (n>0&&!(1162261467%n)) cout<<"true";
else cout<<"false";
return 0;
}
Problem 6
喜羊羊和灰太狼用几堆石子在做游戏。共有偶数堆石子排成一行,每堆都有正整数颗石子
piles_i 。石子的总数是奇数所以没有平局。喜羊羊和灰太狼轮流进行,喜羊羊先手。 每回合玩家从行的开始或结束处取走整堆石头。 不能取石子时手中石子最多的玩家获胜。求喜羊羊是否具有必胜策略。
解答
考虑 dp。
我们使灰太狼得分时,都会从喜羊羊的分数中扣除,这样只需比较最后结果即可。
令
先考虑喜羊羊只能取
分类讨论喜羊羊选取的石子堆:
- 选择
plies_i :dp_{i,j}=plies_i+dp_{i+1,j} ; - 选择
plies_j :dp_{i,j}=plies_j+dp_{i,j-1} 。
喜羊羊得分要最大化,即:
灰太狼同理:
灰太狼注意:
- 得分只有前面有负号,因为转移性质。
- 得分最小化
根据
解答
因为石头的数量是奇数,因此只有两种结果,输或者赢。
喜羊羊先开始随便拿石头,然后比较石头数量:
- 如果石头数量多于对手,赢了;
- 如果石头数量少于对手,自己拿石头的顺序和对手拿石头的顺序对调,还是赢。
所以喜羊羊必胜。
Problem 7
给定整数
n ,求n! 尾数中零的数量。
要求:时间复杂度\mathcal O(n\log n) 。
尾数零考虑分解因子
显然
每次
#include<iostream>
using namespace std;
int main()
{
int n,ans=0;
cin>>n;
while (n) n/=5,ans+=n;
cout<<ans;
return 0;
}
Problem 8
你有
4 张写有1 到9 数字的牌。你需要判断是否能通过*,/,+,-,(,)的运算得到24 。
- 除法运算符
/表示实数除法,而不是整数除法。例如4 / (1 - 2/3) = 12 。- 每个运算符对两个数进行运算。特别是我们不能用
-作为一元运算符。例如,[1, 1, 1, 1] 作为输入时,表达式-1 - 1 - 1 - 1 是不允许的。- 你不能将数字连接在一起。例如,输入为
[1, 2, 1, 2] 时,不能写成12 + 12 。
就四张牌,打表进省一:
人类智慧知,无解情况如下:
1111, 1112, 1113, 1114, 1115, 1116, 1117, 1119, 1122,
1123, 1124, 1125, 1133, 1159, 1167, 1177, 1178, 1179,
1189, 1199, 1222, 1223, 1299, 1355, 1499, 1557, 1558,
1577, 1667, 1677, 1678, 1777, 1778, 1899, 1999, 2222,
2226, 2279, 2299, 2334, 2555, 2556, 2599, 2677, 2777,
2779, 2799, 2999, 3358, 3388, 3467, 3488, 3555, 3577,
4459, 4466, 4467, 4499, 4779, 4999, 5557, 5558, 5569,
5579, 5777, 5778, 5799, 5899, 5999, 6667, 6677, 6678,
6699, 6777, 6778, 6779, 6788, 6999, 7777, 7778, 7779,
7788, 7789, 7799, 7888, 7899, 7999, 8888, 8889, 8899,
8999, 9999
为了装逼,用 van 国码(Unicode)加密一下:
对撒剘劥圞剜劏哱掶桺泛揋掵従剟剣彫寣污悫壛梄甏咍哲汭剤堧点卋嬞勆叛汬泐塵栋劚嚮咃宠吖剗楗囧力桻攋壯劯嗏桹劙剢剚焧啫栕炸栫栖嚲彳剛撑烃洿宋汷彲剙揁妷埻撧汢吩壙劇剭埼吕剝汣敯憇勇剥咎囻匓
用个查找就行。
Algorithm 1
平方根倒数速算法,字面意思,做法玄学。
此算法首先接收一个
32 位带符浮点数,然后将之作为一个32 位整数看待,将其右移一次(取半),并用十六进制“毒瘤数字”0x5f3759df减之,如此即可得对输入的浮点数的平方根倒数的首次近似值;而后重新将其作为原来的浮点数,以牛顿迭代法反复迭代,以求出更精确的近似值,直至求出符合精确度要求的近似值。在计算浮点数的平方根倒数的同一精度的近似值时,此算法比直接使用浮点数除法要快四倍。
这是百度百科上的。
在利用到 3D 图形渲染的一些游戏或者程序当中,法向量的单位化计算显得尤为重要。在三维欧几里得空间当中,某一向量
Code:
float I_am_fun(float x) //float 精度感人
{
float x2=x*0.5f;
int i=*(int*)&x;
i=0x5f3759df-(i>>1);
x=*(float*)&i;
x=x*(1.5F-(x2*x*x));
return x;
}
这个方法从本质上来介绍的话,就是先近似计算一个初始值,然后带入牛顿迭代法来计算最后的较为精确的 i=*(int*)&x 是为了获得浮点数 i=0x5f3759df-(i>>1) 的作用。求得了整数表示 0x5f3759df 的重要意义所在。具体的数学推导以及中间的“毒瘤数字” 0x5f3759df 是怎么算出来的可以参见维基百科。
Operation 1
A+B 的奇异解法。
二进制模拟。
假设有两个二进制数
考虑进位,只需要同一位两个数字都是
#include<iostream>
namespace My_APlusB
{
static bool OutputTheCommandInput=false;
#include<cstring>
#include<cstdio>
using namespace std;
namespace Command
{
inline void trytohelp(){puts("try to input '/help'.");}
void help()
{
puts("使用 command(const char* str) 来使用命令行")
puts("使用 add(int a,int b) 来获取 a+b 的值");
puts("使用 /help 来寻求帮助\n");
puts("内部变量如下:");
puts("\t1. OutputTheCommandInput,终端输出输入");
puts("您可以自己调用 namespace 修改,也可以使用/change 命令修改");
puts("使用方法:/change 变量名 值,当然你也可以使用 default 值来回到初始状态,特殊的,/change default 会使所有变量回到初始状态");
}
}
void command(const char* str)
{
using namespace Command;
if (OutputTheCommandInput) cout<<"<<"<<str<<'\n';
if (str[0]!='/'){puts("[Syntax Error]The command first character must \'/\'");trytohelp();return ;}
if (!strcmp(str,"/help"))
{help();return ;}
if (!strcmp(str,"/change OutputTheCommandInput true")) {OutputTheCommandInput=true;puts("sure");return ;}
if (!strcmp(str,"/change OutputTheCommandInput false")) {OutputTheCommandInput=false;puts("sure");return ;}
if (!strcmp(str,"/change OutputTheCommandInput default")) {OutputTheCommandInput=false;puts("sure");return ;}
if (!strcmp(str,"/change default"))
{
OutputTheCommandInput=false;
puts("sure");
return ;
}
puts("[Syntax Error]Unknown Command.");trytohelp();
} //请自动忽略上面
int add(int a,int b)
{
while (b){int tmpa=a^b,tmpb=(a&b<<1);a=tmpa,b=tmpb;}
}
}