牛客NC21477 御坂美琴

本文解析了一个关于misaka如何通过操作将玩偶分成特定数量堆的问题,介绍了错误的二分拆分思路和正确的深度优先搜索算法。错误思路中的粗暴方法在样例中失效,而正确代码通过dfs确保每一步操作的有效性和目标堆的可达性。
摘要由CSDN通过智能技术生成

 题目描述:

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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值