牛客网暑期ACM多校训练营(第三场)

36 篇文章 0 订阅
34 篇文章 3 订阅

https://www.nowcoder.com/acm/contest/141#question

A

题意:n件物品,每个物品有4个参量p,a,c,m,的需求量及价值g,最后一行输入PACM的存在量。求能取的最大价值。

思路:dfs暴力搜索,,加上一步剪枝就可以了,,至于题解是个dp,背包的,就是一个包变成了四个包。。整体不难,详见代码:

代码:

#include<bits/stdc++.h>
using namespace std;
int n,sum[55],look[55],P,A,M,C,ans,pp[55],x,a[10];
struct AA
{
    int p,a,c,m,g;
}pos[50];
int dfs(int num,int all,int z)
{
    if(num>n) {if(all>ans){
            ans=all;x=z;
    for(int i=1;i<=n;i++) pp[i]=look[i];}return 0;
    }
 
    if(sum[n]-sum[num-1]+all<=ans) return 0;
    if(pos[num].p+a[1]<=P&&pos[num].a+a[2]<=A&&pos[num].c+a[3]<=C&&pos[num].m+a[4]<=M)
    {
        look[num]=1;
        a[1]+=pos[num].p;
        a[2]+=pos[num].a;
        a[3]+=pos[num].c;
        a[4]+=pos[num].m;
        dfs(num+1,all+pos[num].g,z+1);
        a[1]-=pos[num].p;
        a[2]-=pos[num].a;
        a[3]-=pos[num].c;
        a[4]-=pos[num].m;
        look[num]=0;
    }
    if(sum[n]-sum[num]+all<=ans) return 0;
    dfs(num+1,all,z);
    return 0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d%d",&pos[i].p,&pos[i].a,&pos[i].c,&pos[i].m,&pos[i].g);
        sum[i]=sum[i-1]+pos[i].g;
    }
    scanf("%d%d%d%d",&P,&A,&C,&M);
 
    dfs(1,0,0);
    printf("%d\n",x);
    for(int i=1;i<=n;i++)
    {
        if(pp[i]) printf("%d ",i-1);
    }
    printf("\n");
}

E

题意:n长度的字符串,将字符串从0位置长度为0--n-1的串挪到字符串后面,形成n个字符串,标号是挪动的长度,然后相同的字符串是一组,先输出总组数,按标号字典序输出每一组,每组一行,前面先输出一个数表示本组的字符串个数,紧跟着输出对应字符串的标号---字典序

思路:画图理解一下,发现凡是能形成相同的串的,字符串一定有结数>1的循环节,而且每一组数量一样,且都为等差为循环节长度的等差序列。所以就转换成了求字符串的循环节的操作。。用到了KMP 里的next数组的求法

代码:

#include<bits/stdc++.h>
using namespace std;

string P,s;//模板串
int f[1000015];

void getFail()//得到匹配数组
{
    int m =P.length();
    f[0]=f[1]=0;
    for(int i=1;i<m;i++)
    {
        int j=f[i];
        while(j && P[i]!=P[j]) j=f[j];
        f[i+1]=(P[i]==P[j])?j+1:0;
    }
}
int main()
{
    cin>>s;
    P=s;
    getFail();
    int i,j;
    int n=P.length();
    int l=n-f[n];//循环节长度

    if(P.length()%l)//若不能整除,说明除去前缀后缀相同的部分,中间不循环
    {
        l=n;
    }
    printf("%d\n",l);//画图推出输出格式
    for(i=0;i<l;i++)
    {
        printf("%d %d",n/l,i);
        for(j=1;j<n/l;j++)
        {
            printf(" %d",i+l*j);
        }
        printf("\n");
    }
    return 0;
}

H

题意:找到n内的(i,j)的对数,有i/gcd(i,j)和j/gcd(i,j)都是素数,(i,j)符合(j,i)一定符合

思路:先筛素数,然后n个数从1到n枚举,假设是gcd,求两个素数分别与gcd的乘积就是i,j,又有i,j<=n,所以二分找到i,j能取的最大值,小于等于这个数的都能取,从中任取两个分配给i和j,

而且随着枚举的gcd越大,能取的最大的素数一定越来越小,就可以缩小二分范围

代码:

#include<bits/stdc++.h>
using namespace std;
#define M 10000000
int prime[M+5];
long long p;
bool vis[M+5];
long long ans;
int num;
void AA()
{
    int i,j;
    num=0;
    for(i=2;i<M;i++)
    {
        if(!vis[i])
            prime[num++]=i;
        for(j=0;j<num;j++)
        {
            if(i*prime[j]>M)
                break;
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
                break;
        }
    }
}
int erfen(int x)
{
    int l=0,r=p+1,mid=-1;
    while(1)
    {
        if(mid==(l+r)/2)break;
        else
        {
            mid=(l+r)/2;
            if(prime[mid]<=x) l=mid;
            else r=mid;
        }
    }
    return l;
}
int main()
{
    int n;
    AA();
    p=num-1;prime[num]=M;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        p=erfen(n/i);
        ans+=p*(p+1);
    }
    printf("%lld\n",ans);

    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值