Codeforces Round #552 (Div. 3) ABCDEFG

A. Restoring Three Numbers

给出a+b, a+c, b+c  a+b+c 顺序不固定,求a,b,c的值

没什么好说的,找到最大的数就是a+b+c,再减去其他三个数

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

long long num[4];
int main()
{
    long long a,b,c;
    for(int i=0;i<4;i++)
        scanf("%lld",&num[i]);
    sort(num,num+4);
    a=num[3]-num[0];
    b=num[3]-num[1];
    c=num[3]-num[2];
    cout<<a<<' '<<b<<' '<<c<<endl;
}

B. Make Them Equal

给出一个序列,问是否存在d,对于序列每个数ai,进行+d,-d,或不变,使得序列所有数相等

由于ai<=100,直接暴力枚举

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

int num[105];
int main()
{
    int i,j,k,n,d;
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&num[i]);
    }
    for(d=0;d<=100;d++)
    {
        int p=num[0]+d;
        for(i=1;i<n;i++)
            if((abs(num[i]-p)!=d)&&(num[i]!=p))
                break;
        p=num[0]-d;
        for(j=1;j<n;j++)
            if((num[j]!=p)&&(abs(num[j]-p)!=d))
                break;
        p=num[0];
        for(k=1;k<n;k++)
            if((num[k]!=p)&&(abs(num[k]-p)!=d))
                break;
        if(i==n||j==n||k==n)
            break;
    }
    if(d!=101)
        printf("%d\n",d);
    else
        printf("-1\n");
}

C. Gourmet Cat

a,b,c三种食物,星期一,四,七吃a,星期二,六吃b,星期三,五吃c,每天吃一份食物,出发时间可以是任意天,求最大出行天数

一个星期a-=3,b-=2,c-=2;先算出能撑多少个星期,再枚举出发是星期几,求最大出行天数

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

int main()
{
    long long a,b,c;
    long long a1,b1,c1;
    long long num=0;
    long long week,day,ans;
    scanf("%lld%lld%lld",&a,&b,&c);
    week=min(min(a/3,b/2),c/2);
    day=week*7;
    ans=day;
    a-=week*3;
    b-=week*2;
    c-=week*2;
    for(int i=1;i<=7;i++)
    {
        a1=a;
        b1=b;
        c1=c;
        day=week*7;
        for(int j=0;j<7;j++)
        {
            int k=(i+j)%7;
            if(k==0)
                k=1;
            if(k==1||k==4||k==7)
            {
                if(a1>0)
                    day++,a1--;
                else
                    break;
            }
            else if(k==2||k==6)
            {
                if(b1>0)
                    day++,b1--;
                else
                    break;
            }
            else if(k==3||k==5)
            {
                if(c1>0)
                    day++,c1--;
                else
                    break;
            }
        }
        ans=max(day,ans);
    }
    printf("%lld\n",ans);
}

D. Walking Robot

一个机器人,有一块主能源和备用能源,每次前进一个单位消耗一个单位能源,当在太阳下使用主能源(主能源不为0)时,备用能源增加一个单位(备用能源不能超过初始值,主能源不能充电),求最大行进距离(0代表阴影区,1代表太阳下)

备用能源满时则用备用能源

备用能源不满时,在太阳下优先用主能源,在阴影下优先用主能源

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

int state[N];
int main()
{
    int i,j,n,a,b,w=0;
    int bmax;
    scanf("%d%d%d",&n,&a,&b);
    bmax=b;
    for(i=0;i<n;i++)
        scanf("%d",&state[i]);
    for(i=0;i<n;i++)
    {
        if(a==0&&b==0)
            break;
        w++;
        if(state[i]==1)
        {
            if(b!=bmax)
            {
                if(a!=0)
                {
                    a--;
                    b++;
                }
                else
                    b--;
            }
            else
                b--;
        }
        if(state[i]==0)
        {
            if(b!=0)
                b--;
            else
                a--;
        }
    }
    printf("%d\n",w);
}



E. Two Teams

两个球队教练轮流选队员,1号教练在球员中选出最优秀的球员,并且把该球员左边k个球员(不足k个则取完)和右边k个球员选入1队,接着2号教练在剩下的的球员中采用同样的操作,然后又是1号教练......问最后每个球员属于哪个队。

 

解法很多,有用并查集的,有用链表的,还有用线段树写的(比如说我队友,有点头铁),我是通过记录下一条的方法写的;

建立一个rk数组,记录能力值排名i的球员的下标位置,并且维护next数组,保存当该名球员已经被选中,应该跳向哪个位置数组的哪个位置

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

int state[N],rk[N],val[N];
int nx1[N],nx2[N];
//nx1往左下一跳
//nx1往右下一跳
int main()
{
    int n,i,j,k,flag=0;
    scanf("%d%d",&n,&k);
    for(i=0;i<n;i++)
    {
        scanf("%d",&val[i]);
        rk[n-val[i]]=i;
    }
    for(i=0;i<n;i++)
        nx1[i]=i;
    for(i=0;i<n;i++)
        nx2[i]=i;
    for(i=0;i<n;i++)
    {
        int id=rk[i];//排名i的数字下标
        if(state[id]!=0)//被选了
            continue;
        if(flag==0)
            state[id]=1;
        else
            state[id]=2;
        int p=k;
        int k1=1;
        j = id;
        while(p)
        {
            if(j-k1<0)
                break;
            if(state[j-k1]!=0)
            {
                k1=j-nx1[j-k1];
            }
            else
            {
                if(flag==0)
                    state[j-k1]=1;
                else
                    state[j-k1]=2;
                k1++;
                p--;
            }
        }
        int x=j-k1;//左边界
        k1 = 1;
        p = k;
        j = id;
        while(p)
        {
            if(j+k1>=n)
                break;
            if(state[j+k1]!=0)
            {
                k1=nx2[j+k1]-j;
            }
            else
            {
                if(flag==0)
                    state[j+k1]=1;
                else
                    state[j+k1]=2;
                k1++;
                p--;
            }
        }
        int y=j+k1;//右边界
        nx1[y-1]=x;
        nx2[x+1]=y;
        flag++;
        flag%=2;
    }
    for(i=0;i<n;i++)
        printf("%d",state[i]);
    printf("\n");
}

F. Shovels Shop

给出n个物品,从中购买k个物品,有m种优惠方案:(x1,y1),(x2,y2)......(xm,ym)代表当购买xi个物品时间,最便宜的yi个免费,求支付的最小价格。

被队友误导了,口胡成完全背包(没错,就是那个用线段树写E题的那个人),虽然的确是一道dp题

因为我们是取买k个最小的代价,那么我们只要用到最小的k个物品;进行dp之前有两个结论要证明

(1)对于优惠值为yi,若存在yi==yj && xi>xj,如果我们对p个数用过(xi,yi)方案,那么我们把相同y值的而x更小的方案(xi,yi)替换(xj,yj),剩下的xi-xj个数可以尝试选择优惠方案来减小代价,所以相同y值(xj,yj)比(xi,yi)更优

 (2)对于排序好的p个数,使用两种方案(xi,yi),(xj,yj),xi+xj==p,取出p个数前面xi个使用优惠(xi,yi),再对剩下xj个数使用优惠(xj,yj)的代价( 设这里先用(xi,yi)再用(xj,yj)代价小于先用(xj,yj)再用(xi,yi) )小于取出中间xi个使用(xi,yi)优惠,再对剩下xj个使用(xj,yj)优惠的代价要低(大家可以自己推一下),由此递推出使用q种优惠也是每次取出最大的几个

接着我们预处理一下,求出对于每个y值,最小的x是什么,然后dp

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

int cost[N],a[N];
int dp[2005];
int main()
{
    int i,j,n,m,k,p,ans=0;
    int x,y;
    scanf("%d%d%d",&n,&m,&k);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+n+1);
    n = k;
    for(i=0;i<m;i++)
    {
       scanf("%d%d",&x,&y);
       if(cost[y]==0)
           cost[y]=x;
       else
           cost[y]=min(cost[y],x);
    }
    dp[0]=0;
    for(i=1;i<=n;i++)
        dp[i]=dp[i-1]+a[i];
    for(i=1;i<=n;i++)//取出前i个的数的最小代价
    {
        for(j=0;j<=i;j++)
        {
            x=cost[j];
            y = j;
            //取出后面x个数,最小的y的免费
            p = 0;
            if(x>i)
                continue;
            for(k=i-(x-y)+1;k<=i;k++)
                p+=a[k];
            dp[i]=min(dp[i],dp[i-x]+p);
        }
    }
    cout<<dp[n]<<endl;
}

G. Minimum Possible LCM

求n个数中哪两个数的最小公倍数最小,先上图。。。

     我可能连傻逼都不如吧。。。

按照cls的说法枚举gcd,时间复杂度为n+n/2+n/3+.....n/n-1+n/n ≈ n*logn,高数学的差,调和级数都不会算,还以为是O(n^2)

用两个数组存放值为ai出现的第一个位置,和另一个位置(如果出现过两次及以上),然后就可以枚举了

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

int state[N];
int state1[N];
long long gcd(long long x,long long y)
{
    if(y==0)
        return x;
    return gcd(y,x%y);
}
int main()
{
    int i,j,x,n,Max=0,num=0;
    int ans1,ans2;
    long long ans=0x3f3f3f3f3f3f3f3f;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if(state[x]==0)
            state[x]=i;
        else
            state1[x]=i;
        Max=max(Max,x);
    }
    long long a,b;
    for(i=1;i<=Max;i++)//枚举gcd
    {
        num = 0;
        for(j=i;j<=Max;j+=i)//枚举最小倍数
        {
            if(state[j]!=0)
            {
                if(num==0)
                    a=j;
                else
                    b=j;
                num++;
            }
            if(state1[j]!=0)
            {
                b=j;
                num=2;
            }
            if(num==2)
            {
                if(ans>a*b/gcd(a,b))
                {
                    ans=a*b/gcd(a,b);
                    ans1=state[a];
                    ans2=state[b];
                    if(state1[b]!=0)
                        ans2=state1[b];
                }
                break;
            }
        }
    }
    if(ans1>ans2)
        swap(ans1,ans2);
    cout<<ans1<<' '<<ans2<<endl;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值