牛客小白月赛94部分题解

前言

本次小白月赛据说是AK题,但是蒟蒻考场只做了前三题打卡题,考后优化一下出了第四题,还是太弱了,下面是一些题解。

题解

第一题 小苯的九宫格

打卡题,没什么难度,我利用map打出来了,其实就是对1-9进行重新赋值

#include <bits/stdc++.h>
using namespace std;
int main()
{
	string s;
	int x;
	map<int,int>a;
	for(int i=1;i<=9;i++)
	{
		cin>>x;
		a[i]=x;
	}
	cin>>s;
	for(int i=0;i<s.length();i++)
	{
		cout<<a[s[i]-48];
	}
	return 0;
}

第二题 小苯的好数组

需要注意的是不完全相同,所以其实只要出现有逆序的情况就可以将整个串当成好数组,故实际上结果要么是0要么是n,当数组为不减数组或者单个数组时,为0,否则则为1

#include <bits/stdc++.h>
using namespace std;
int a[3000005];
int main()
{
	int n;cin>>n;
	if(n==1) {cout<<0;return 0;}
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	for(int i=0;i<n-1;i++)
	{
		if(a[i]>a[i+1]){cout<<n;return 0;}
	}
	cout<<0;
	return 0;
}

第三题 小苯的数字合并

本题我觉得苯人是蒙出来歪打正着了,最开始是只求每个数的后缀和,然后让后缀和去减去前面的a[i-1]然后一个一个求出其中最大值,因为要求极差最大则是让最大值更大,最小值更小,在这种方法里,我将所有的后缀和加起来,让最大值越来越大,且因为是正数,最小数一定是某个单独的元素,并且去逐个求极差,但是后来发现,其实前缀和也要求,毕竟即使ai=ai+ai+1;也可以看作a1=a1+a2变成a2了1,3,5->4,5,反正就是也要求前缀和,然后再一个一个用同样方法求极差,这样前后一起走,就AC了;

#include <bits/stdc++.h>
using namespace std;
long long a[3000005],s[3000005];//a为数组,s为前/后缀和
int main()
{
	long long n,ans=0;cin>>n;
	if(n==1){cout<<0;return 0;}
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	s[n]=a[n];
	for(int i=n-1;i>=1;i--)
	{
		s[i]=s[i+1]+a[i];//求后缀和
	}
	for(int i=2;i<=n;i++)
	{
		ans=max(ans,abs(s[i]-a[i-1]));//比较求极差最大值
	}
	s[1]=a[1];
	for(int i=2;i<=n;i++)
	{
		s[i]=s[i-1]+a[i];//求前缀和
	}
	for(int i=1;i<n;i++)
	{
		ans=max(ans,abs(s[i]-a[i+1]));//同理
	}
	cout<<ans;
	return 0;
}

看了题解之后,他说应该是找某一个最小数的前后的前缀和和后缀和,并且看哪个大,其实就是把我的算法结合了一下,基于最小值一定是某个值,遍历所有情况的思路。需要注意的是,这里一定要用前缀和和后缀和,不然可能会超时。

第四题 小苯的排列构造

        

这看起来像是数论的问题,我完全沉到了之前见过的一道无锡学院之前出过的题,以为是利用这个结论啥的,结果发现没啥关系

这道题我们首先可以知道ai-1和pi一起得到了ai,即ai=gcd(ai-1,pi),其实没啥用,但是我们可以由最小公倍数的定义得到关于pi一定是ai的倍数关系,以及ai一定不增的,因为一直在与ai-1求最小公倍数,并且最后一定会减少到1,然后因为gcd(ai,1)=1就一直为1了;所以我们只需要考虑ai!=1的情况就像对于例子4211得到4213,则4=4*1,2=2*1.

对于6333111

6=6*1 , 3=3*1,9=3*3(因为这里3*2=6已经被用过了,所以用3*3=9),12=3*4,然后1之后只要随便输出之前没输出过得数就行,所以我们只需要对ai!=1的ai一直乘就可以求出来pi,但是如果

(下面是优化时间,比赛因为没优化,过了91%卡死了)

对于9  3 3 3******省略一万个3*** 1 1 1 1我们每次都要从3*1,3*2.........3*n直到找到之前没出现过的数,但是其实我们知道后面的数其实不需要再经历3*1,3*2.....只要从上一个数开始找就可以了,会爆掉的,所以这里我用map存储这个3*n已经乘了几次了即n的大小,就过了

#include <bits/stdc++.h>
using namespace std;
int a[200001],s[200001],ans[200001];//a[i]是ai,s[i]是标记i用过了没,ans[i]是pi即答案
int main()
{
	map<int,int> c;//c是表示对于一个ai他的对应的pi已经是pi=ai*c,所乘的c的大小
    int n,sign=0;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=2;i<=n;i++){
        if(a[i-1]%a[i]){cout<<-1;return 0;}//如果不是最小公倍数则不成立直接-1
    }
    ans[1]=a[1];s[a[1]]=1;
    for(int i=2;i<=n;i++)
    {
        sign=0;
        if(a[i]!=1)//对ai不是1的部分进行每次ai乘c直到找到pi的操作
        {
            if(c[a[i]])c[a[i]]++;
            else c[a[i]]=1;
            while(s[a[i]*c[a[i]]]&&a[i]*c[a[i]]<=n){c[a[i]]++;}
            ans[i]=a[i]*c[a[i]];s[a[i]*c[a[i]]]=1;
        }
        else {sign=i-1;break;}
    }
    cout<<ans[1];
    for(int i=2;i<=sign;i++)
    {
        cout<<' '<<ans[i];
    }
    for(int i=1;i<=n;i++)
    {
        if(!s[i])cout<<' '<<i;//对是1的部分直接输出未输出的,不用考虑第一个是1的Pi是不是与a(i-1)为gcd=1,因为一定是1
    }
    return 0;
}

不存在的条件就是ai和ai-1不能整除,那样必然不是 ai=gcd(ai-1,pi),就输出-1即可

第五题 小苯的01背包(easy)

这道题是补出来的,用的题解的期望值贪心法,因为最后的价值为所有价值的与,越与越小的道理,则最后的答案一定小于最大价值,小于2000,则每次枚举价值,判断背包是否小于等于k(反向枚举,直接求出最大值就走)。注意如果没有则输出0。

	#include <bits/stdc++.h>
	using namespace std;	
	int n,k,v[20000],w[20000],sign,b;//v体积,w价值
	int main()
	{
		cin>>n>>k;
		for(int i=0;i<n;i++)
		{cin>>v[i]>>w[i];}
		for(int i=2000;i>=0;i--)//枚举
		{
			sign=1;//所选物品容量b
			b=2001;//避免一次没改直接输出i=2000
			for(int j=0;j<n;j++)
			{
				if((i&w[j])==i)
				{
					if(sign){b=v[j];sign=0;}//第一次直接付给b
					else b&=v[j];//之后按位与
				}
			}
/*			
			此方法不用sign,利用二进制位全为1
			b=0xffffffff;//b的二进制位1111111111111111全为1故第一次与得到的就是第一个v[i]
			for(int j=0;j<n;j++)
			{
				if((i&w[j])==i)
				{
					b&=v[j];
				}
			}
*/
			if(b<=k) {cout<<i;return 0;}
		}
		cout<<0;//如果没有则输出0
		return 0;
	}

第六题小苯的01背包(hard)

用试填法,对最后的答案每一位看可否是1,如果上一个可以是1,那就在可以是1的部分里找出下一位的东西,一个学长的代码放这里,有空再补

    #include <bits/stdc++.h>
#define int long long
#define pll pair<long long,long long>
#define fi first
#define se second
using namespace std;

const int N=1e6+10,mod=998244353;
int n,m,k;
int res;

int num[N];
int ans[N];

int v[N],w[N];
int book[N];

signed main()
{
    cin>>n>>m;

    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    bool flag=1;
    for(int o=32;o>=0;o--)
    {
        int tmp=0,ct=0;
        for(int i=1;i<=n;i++)
        {
            if(book[i]) continue;
            if((w[i]>>o)&1)
            {
                if(ct==0) tmp=v[i];
                else
                tmp&=v[i];
                ct++;
            }
        }
        if((ct)&&(tmp<=m))
        {
            res|=(1<<o);
            for(int i=1;i<=n;i++)
            {
                if(!((w[i]>>o)&1)) book[i]=1;
            }
        }
    }
    cout<<res<<endl;
}
/**

*/

  • 38
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值