题目描述:
misaka是呱太爷爷的小粉丝,呱太爷爷有一句话说的好:"一尺之棰,日取其半,万世不竭"。
misaka现在有 n 个呱太玩偶放在一堆,每一次操作,misaka会选择当前个数 > 1 的一堆呱太玩偶。并将这一堆呱太玩偶分成 ⌊x/2⌋和 x−⌊x/2⌋ 两堆,x 是当前这一堆玩偶的个数。现在 misaka 想将玩偶分成 m 堆,其中第 i 堆呱太玩偶的个数是 ai ,你需要告诉 misaka 是否能通过若干次操作将玩偶分成指定的这 m 堆。如果可以输出 misaka ,否则输出 ham 。
思路:
先检验输入的数组之和是否等于n,如果等于就对n进行dfs,不断分成⌊x/2⌋和 x−⌊x/2⌋ 两堆,检验是否可以和数组中的数匹配。为了方便查找,数组中的数可以用map储存。
思路不难,写题解是因为看别人代码的时候发现很多人AC的算法是错误的,但是题目的样例并没有检测出来。
/* 至于看别人代码是因为我个菜狗一开始啥思路没有这件事暂且略过不提 */
错误的思路如下:先把n不断二分,所有能拆出的数字都记录下来。如果每一个输入的数字都能够从n中拆分出来,并且它们之和等于n,输出misaka。
//错误思路
#include <iostream>
#include <map>
using namespace std;
map<long long,long long> a;
void dfs(long long x)
{
if(a[x]==1) return;
a[x]=1;
dfs(x/2);
dfs(x-x/2);
}
int main()
{
int m;
long long n;
scanf("%lld%d",&n,&m);
dfs(n);
int flag=0;
long long t,sum=0;
for(int i=0;i<m;i++)
{
scanf("%lld",&t);
sum+=t;
if(!a[t]) flag=1;
}
if(flag||sum!=n) printf("ham");
else printf("misaka");
return 0;
}
直觉上很有漏洞,这也太粗暴。
用样例进行检验。
14 4
1 3 3 7
这组样例明显是错误的,应该输出ham,但是按照上面的思路就会输出misaka。
/* 至于看着这个错误的思路理解了半小时为什么能够AC这件事也略过不提 */
正确代码如下:
//正确思路
#include<bits/stdc++.h>
using namespace std;
map<long long,int>cnt;
void dfs(long long n)
{
if(!n)return ;
if(cnt[n]){--cnt[n];return ;}
if(n==1){puts("ham");exit(0);}
dfs(n/2);dfs(n-n/2);
}
int main()
{
long long n;int m;
cin>>n>>m;
long long sum=n;
while(m--)
{
long long x;
scanf("%lld",&x);
++cnt[x];
sum-=x;
if(sum<0){puts("ham");exit(0);}
}
if(sum){puts("ham");exit(0);}
dfs(n);
puts("misaka");
}