CodeM美团点评编程大赛资格赛

[编程题] 音乐研究

自己太渣,就做了最水的两题,然后借鉴了两题别人的代码,因为觉得代码比较好,因此收藏一下,时常看看,提高水平。


时间限制:1秒

空间限制:32768K

美团外卖的品牌代言人袋鼠先生最近正在进行音乐研究。他有两段音频,每段音频是一个表示音高的序列。现在袋鼠先生想要在第二段音频中找出与第一段音频最相近的部分。

具体地说,就是在第二段音频中找到一个长度和第一段音频相等且是连续的子序列,使得它们的 difference 最小。两段等长音频的 difference 定义为:
difference = SUM(a[i] - b[i]) 2 (1 ≤ i ≤ n),其中SUM()表示求和 
其中 n 表示序列长度,a[i], b[i]分别表示两段音频的音高。现在袋鼠先生想要知道,difference的最小值是多少?数据保证第一段音频的长度小于等于第二段音频的长度。

输入描述:
第一行一个整数n(1 ≤ n ≤ 1000),表示第一段音频的长度。
第二行n个整数表示第一段音频的音高(0 ≤ 音高 ≤ 1000)。
第三行一个整数m(1 ≤ n ≤ m ≤ 1000),表示第二段音频的长度。
第四行m个整数表示第二段音频的音高(0 ≤ 音高 ≤ 1000)。


输出描述:
输出difference的最小值

输入例子:
2
1 2
4
3 1 2 4

输出例子:
0
 
  
#include<iostream>
#include<cmath>
using namespace std;
int a[1001],b[1001];
long long mm=1000000000,hh;
int main()
{
	int n,m;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	cin>>m;
	for(int j=0;j<m;j++)
		cin>>b[j];
	for(int i=0;i<=m-n;i++)
	{
		hh=0;
		for(int j=0;j<n;j++)
		{
			hh+=(b[i+j]-a[j])*(b[i+j]-a[j]);
		}
		if(hh<mm)
			mm=hh;
	}
	cout<<mm<<endl;
	return 0;
}

[编程题] 锦标赛

时间限制:1秒

空间限制:32768K

组委会正在为美团点评CodeM大赛的决赛设计新赛制。

比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛、复赛的成绩,会有不同的积分。比赛采取锦标赛赛制,分轮次进行,设某一轮有 m 个人参加,那么参赛者会被分为 m/2 组,每组恰好 2 人,m/2 组的人分别厮杀。我们假定积分高的人肯定获胜,若积分一样,则随机产生获胜者。获胜者获得参加下一轮的资格,输的人被淘汰。重复这个过程,直至决出冠军。

现在请问,参赛者小美最多可以活到第几轮(初始为第0轮)? 
输入描述:
第一行一个整数 n (1≤n≤ 2^20),表示参加比赛的总人数。

接下来 n 个数字(数字范围:-1000000…1000000),表示每个参赛者的积分。

小美是第一个参赛者。


输出描述:
小美最多参赛的轮次。

输入例子:
4
4 1 2 3

输出例子:
2

#include<iostream>
using namespace std;
long long n,cnt,num;
int t;
int main()
{
	cin>>n;
	cin>>num;
	n--;
	while(n--)
	{
		int temp;
		cin>>temp;
		if(temp<=num)
			cnt++;
	}
	cnt++;
	while(cnt!=1)
	{
		cnt/=2;
		t++;
	}
	cout<<t<<endl;
	return 0;
}


   
   
[编程题] 送外卖

时间限制:1秒

空间限制:32768K

n 个小区排成一列,编号为从 0 到 n-1 。一开始,美团外卖员在第0号小区,目标为位于第 n-1 个小区的配送站。 给定两个整数数列 a[0]~a[n-1] 和 b[0]~b[n-1] ,在每个小区 i 里你有两种选择: 1) 选择a:向前 a[i] 个小区。 2) 选择b:向前 b[i] 个小区。 把每步的选择写成一个关于字符 ‘a’ 和 ‘b’ 的字符串。求到达小区n-1的方案中,字典序最小的字符串。如果做出某个选择时,你跳出了这n个小区的范围,则这个选择不合法。  • 当没有合法的选择序列时,输出 “No solution!”。 • 当字典序最小的字符串无限长时,输出 “Infinity!”。 • 否则,输出这个选择字符串。 字典序定义如下:串s和串t,如果串 s 字典序比串 t 小,则 • 存在整数 i ≥ -1,使得∀j,0 ≤ j ≤ i,满足s[j] = t[j] 且 s[i+1] < t[i+1]。 • 其中,空字符 < ‘a’ < ‘b’。 
输入描述:
输入有 3 行。

第一行输入一个整数 n (1 ≤ n ≤ 10^5)。

第二行输入 n 个整数,分别表示 a[i] 。

第三行输入 n 个整数,分别表示 b[i] 。

−n ≤ a[i], b[i] ≤ n


输出描述:
输出一行字符串表示答案。

输入例子:
7
5 -3 6 5 -5 -1 6
-6 1 4 -2 0 -2 0

输出例子:
abbbb
大神讲解:作者:toraoh
链接:https://www.nowcoder.com/discuss/28562
来源:牛客网

构建反向图(本来是3能到6的,我加一条6到3的边),从n出发,广度优先搜索(bfs)一遍,能到1就有解。
同时记录下哪些点被经过了,这在原图中,就是能到n的点。
有人问了,那什么时候会是无穷长的解?
看下图:

注意我们要 字典序最小,而不是长度最小
那么在3这个位置上,我们要不停地选a,而不能选b,否则,字典序上来讲:
aaba......在aaaa.......之后
这里的处理也比较简单:
1、从1出发,每个点贪心选择:如果走a,这个点能到n,那就走a,否则走b。
2、但如果走回到之前走过的点,那说明答案是Infinity。

作者:toraoh
链接:https://www.nowcoder.com/discuss/28562
来源:牛客网

#include <stdio.h>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

vector<int> G[100005];
int a[100005],b[100005];
int n;
bool vis[100005];
bool vis2[100005];
char str[100005];

void rev_bfs(int p){
    queue<int> q;
    vis[p]=1;
    q.push(p);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=0;i<G[x].size();i++){
            if(!vis[G[x][i]]){
                vis[G[x][i]]=1;
                q.push(G[x][i]);
            }
        }
    }
}

void input(int* a){
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        int tmp=i+a[i];
        if(tmp>=1&&tmp<=n)
            G[i+a[i]].push_back(i);
    }
}

int main(){
    scanf("%d",&n);
    input(a);
    input(b);
    rev_bfs(n);
    if(!vis[1]){
        puts("No solution!");
        return 0;
    }
    int p=0;
    vis2[1]=1;
    bool infflag=0;
    for(int x=1;x!=n&&!infflag;){
        int nxt=x+a[x];
        if(nxt>=1&&nxt<=n&&vis[nxt]){
            if(!vis2[nxt]){
                vis2[nxt]=1;
                str[p++]='a';
            }else{
                infflag=1;
            }
            x=nxt;
        }else{
            nxt=x+b[x];
            if(nxt>=1&&nxt<=n&&vis[nxt]){
                if(!vis2[nxt]){
                    vis2[nxt]=1;
                    str[p++]='b';
                }else{
                    infflag=1;
                }
            }else{
                puts("No solution!");
                return 0;
            }
            x=nxt;
        }
    }
    puts(infflag?"Infinity!":str);
    return 0;
}

[编程题] 数码

时间限制:1秒

空间限制:32768K

给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。 
输入描述:
一行,两个整数 l 和 r (1 ≤ l ≤ r ≤ 10^9)。


输出描述:
输出9行。

第 i 行,输出数码 i 出现的次数。

输入例子:
1 4

输出例子:
4
2
1
1
0
0
0
0
0
大神讲解:作者:toraoh
链接:https://www.nowcoder.com/discuss/28562
来源:牛客网


       
       
这个问题,经典起手式:求[a,b]的问题,拆成求[1,b]-[1,a-1]的问题,而处理[1,x]的问题一般容易搞定不少。
OK,[1,x]怎么处理?
首先要意识到:在[1,x]中,有约数y的数有x/y个。
那么,假设x为常数,对函数f(y)=x/y,在y=1,2,3...n上的点用曲线近似,是反比例函数在第一象限的图像。
(图就靠大家自己画了~)
特点:前半段很陡峭,差个1,函数值都差远了;后半段很平坦,n/2个数的函数值都为1……
根据这两段的不同情况,我们采取不同的处理策略:
对前半段(比如,1到10万),我们直接算有多少个数包含这个约数x,直接加到答案上。
对后半段(10万以上),我们计算有多少个约数,只有1个、2个....个数的约数中包含他,这些数的取值范围是多少。
然后我们统计出这些约数中,有多少是以1、2、3...9开头的数,乘上相应的约数个数,加到答案中。
这样搞定了~基本是一个sqrt(n)*log(n)的时间复杂度。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef long long ll;

ll a[10],b[10],out[10];
int highest[100001];

void cnt(int l,int r){
    memset(out,0,sizeof(out));
    char tmp[15];
    while(l<=r){
        sprintf(tmp,"%d",r);
        int t2=tmp[0]-'0';
        for(int i=1;tmp[i];i++)t2=t2*10;
        if(t2<l)t2=l;
        out[tmp[0]-'0']+=r-t2+1;
        r=t2-1;
    }
}

void get(int x,ll* a){
    if(x==0)return;
    for(int i=1;i<=100000;i++){
        a[highest[i]]+=x/i;
    }
    if(x<=100000)return;
    int last=x;
    for(int i=1;;i++){
        int p2=x/(i+1);
        ++p2;
        if(p2<=100000)p2=100001;
        cnt(p2,last);
        for(int j=1;j<=9;j++)a[j]+=out[j]*i;
        last=x/(i+1);
        if(last<=100000)break;
    }
}


int main(){
    for(int i=1;i<=9;i++){
        for(int j=1;j<=10000;j*=10){
            for(int k=0;k<j;k++){
                highest[i*j+k]=i;
            }
        }
    }
    highest[100000]=1;
    int l,r;
    scanf("%d%d",&l,&r);
    get(r,b);get(l-1,a);
    for(int i=1;i<=9;i++)printf("%lld\n",b[i]-a[i]);
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没想好叫什么名字

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值