牛客网暑期ACM多校训练营(第三场)- (A,C,H)

比赛链接https://www.nowcoder.com/acm/contest/141#question 

A PACM Team

题意:有N个专家团队,第i个团队给出pi,ai,ci,mi,gi分别代表p、a、c、m类型专家的数量以及gi代表团队知识点。要求选取某些专家团队使得p、a、c、m类型的专家总人数不超过P、A、C、M个,而且尽量使得gi之和大。

解析:方法一:直接DFS+剪枝,用时160ms。方法二:标程解法,DP。dp[i][p][a][c][m]表示前i个团队中选取某些团队而且专家的数量不超过p、a、c、m的最多知识点数。状态转移如下:dp[i+1][ip][ia][ic][im]=dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i];

队友代码(DFS):

#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");
}

代码(DP):

#include <bits/stdc++.h>
using namespace std;
const int N=37;

int n,p[N],a[N],c[N],m[N],g[N];
int P,A,C,M;
short dp[N][N][N][N][N];//注意用sort不会爆内存
bool tk[N][N][N][N][N];

int main()
{
    cin>>n;
    for(int i=0; i<n; i++)
        cin>>p[i]>>a[i]>>c[i]>>m[i]>>g[i];
    cin>>P>>A>>C>>M;

    for(int i=0; i<=n; i++)
        for(int ip=0; ip<=P; ip++)
            for(int ia=0; ia<=A; ia++)
                for(int ic=0; ic<=C; ic++)
                    for(int im=0; im<=M; im++)
                        tk[i][ip][ia][ic][im]=false;

    for(int i=0; i<n; i++)
    {
        for(int ip=P; ip>=0; ip--)
            for(int ia=A; ia>=0; ia--)
                for(int ic=C; ic>=0; ic--)
                    for(int im=M; im>=0; im--)
                        dp[i+1][ip][ia][ic][im]=dp[i][ip][ia][ic][im];


        for(int ip=P; ip>=p[i]; ip--)
            for(int ia=A; ia>=a[i]; ia--)
                for(int ic=C; ic>=c[i]; ic--)
                    for(int im=M; im>=m[i]; im--)
                        if(dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i]>dp[i+1][ip][ia][ic][im])
                        {
                            dp[i+1][ip][ia][ic][im]=dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i];
                            tk[i+1][ip][ia][ic][im]=true;//选中i来组合答案
                        }
    }

    //fprintf(stderr, "ans=%d\n", dp[n][P][A][C][M]);
    vector<int> ans;
    for(int i=n; i>=1; i--)
    {
        if(tk[i][P][A][C][M])
        {
            ans.push_back(i-1);
            P-=p[i-1];
            A-=a[i-1];
            C-=c[i-1];
            M-=m[i-1];
        }
    }

    reverse(ans.begin(), ans.end());
    printf("%d\n", (int)ans.size());

    for(size_t i=0; i<ans.size(); i++)
        printf("%d%c", ans[i], "    \n"[i+1==ans.size()]);

    return 0;
}
C Shuffle Cards

题意:初始有长为N的串1~N,对其进行M次操作,每次操作给出L和R,作用是将串中的第L到第L+R-1个字符剪下放到数组前面。

解析:是平衡数的模板题,题目可以用封装好的<rope>来写。

代码

#include<bits/stdc++.h>
#include <ext/rope>
using namespace std;
using namespace __gnu_cxx;
rope<int>s;
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        s.push_back(i);
    int a,b;
    for(int i=0;i<m;i++)
    {
        scanf("%d %d",&a,&b);
        s=s.substr(a-1,b)+s.substr(0,a-1)+s.substr(a+b-1,n-a-b+1);
    }
    for(int i=0;i<n-1;i++)
        printf("%d ",s[i]);

    printf("%d",s[n-1]);
}
H Diff-prime Pairs

题意:给出N,求有多少对(i,j),满足i/gcd(i,j)与j/gcd(i,j)都是素数。注意如果 i1 ≠ i2 或 j1 ≠ j2则 (i1, j1) 与 (i2, j2) 是不同的。

解析:首先筛出N之内的素数。若i/gcd(i,j)是素数,那么i,j就是素数的gcd(i,j)倍,想到我们可以从1~N枚举所有gcd(i,j),那么我们可以二分找到最大的素数maxP满足maxP*gcd(i,j)<=N,由于我们二分时就知道了maxN是第几个数素数(假设为num),那么 (i/gcd(i,j),j/gcd(i,j))就是由前num个素数组合而成,方案数为num*(num-1),每种方案与一种(i,j)一一对应。

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/sdau20163942/article/details/81227354