培训贪心题解

A - Sasha and His Trip

在第一个城市加满,之后每到一个城市加一升油,直到满足最低油耗量。注意有可能在第一个城市就超过最低油耗量。

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

int main (){
    int n,v;
    cin>>n>>v;
    int ans=min(n-1,v);
    int need=n-1-ans;//还需要的油
    ans+=(1+need+2)*need/2;//等差求和公式
    printf("%d\n",ans);
}

 

B - Emotes

使用k次最大值的表情,再使用一次第二大值的表情,循环直到m次。

#include <bits/stdc++.h>
using namespace std;
long long a[200004];
int main (){
    int n,m,k;
    cin>>n>>m>>k;
    for (int i = 0; i < n; ++i) {
        cin>>a[i];
    }
    long long ans=0;
    sort(a,a+n);
    long long max1=a[n-1];
    long long max2=a[n-2];
    ans+=m/(k+1)*(k*max1+max2);//把m分为长度为(k+1)的组
    ans+=m%(k+1)*max1;//剩余的
    printf("%lld\n",ans);
}

C - Kayaking

枚举划单人船的两个人,其余的排序后依次上船即可

#include <bits/stdc++.h>
using namespace std;
int a[104];
int main (){
    int n;
    cin>>n;
    for (int i = 1; i <= 2*n; ++i) {
        cin>>a[i];
    }
    sort(a+1,a+1+2*n);
    int ans=1e9;
    for (int i = 1; i <= 2*n; ++i) {//枚举第一个单人船的人
        for (int j = i+1; j <= 2*n; ++j) {//第二个
            int sum=0;
            for (int k = 1; k <= 2*n; k+=2) {
                while(k==i||k==j)k++;//跳过单人船的人
                int b=k+1;
                while(b==i||b==j)b++;//跳过单人船的人
                if(b<=2*n)
                    sum+=a[b]-a[k];
            }
            ans=min(ans,sum);
        }
    }
    printf("%d\n",ans);
}

 

D - Got Any Grapes?

 先把绿葡萄给Andrew,如果不够的话输出NO,够的话绿葡萄减去x个。

再把绿葡萄和紫葡萄给Dmitry,同上。

最后把所有葡萄给Michal,同上。

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

int main()
{
    int x,y,z,a,b,c;
    cin>>x>>y>>z>>a>>b>>c;
    a-=x;
    if(a<0){
        puts("NO");
        return 0;
    }
    if(a+b<y){
        puts("NO");
        return 0;
    }
    int sum=a+b+c-y;
    if(sum<z){
        puts("NO");
        return 0;
    }
    puts("YES");
}

E - Clarke and food

排序,然后从小到大遍历即可。

#include "bits/stdc++.h"
using namespace std;
int a[100005];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,v;
        scanf("%d%d",&n,&v);
        for (int i = 0; i < n; ++i) {
            scanf("%d",&a[i]);
        }
        sort(a,a+n);
        int ans=0;
        for (int i = 0; i < n; ++i) {
            v-=a[i];
            if(v<0)break;
            else ans++;
        }
        printf("%d\n",ans);
    }
}

F - Two Cakes

每个值出现两次,即每个值对应两个下标,因为必须按照1-n的顺序走,那么每次选择对后面的结果都没有影响。对于每种蛋糕店只有两种方案,每次取最小值即可。详见注释。

#include "bits/stdc++.h"
using namespace std;
int a[200004];//用来记录蛋糕店的下标
int b[200004];
int main()
{
    int n;
    scanf("%d",&n);
    int x;
    memset(a,0, sizeof(a));
    memset(b,0, sizeof(b));
    for (int i = 1; i <= 2*n; ++i) {
        scanf("%d",&x);
        if(a[x]==0)a[x]=i;//a[x]==0说明x这种蛋糕店是第一次出现
        else {//否则就是第二次出现
            b[x]=i;
        }
    }
    long long ans=0;
    a[0]=1,b[0]=1;//初始位置是1
    for (int i = 1; i <= n; ++i) {
        //两种方案,第一种a[i-1]到a[i]和b[i-1]到b[i],第二种a[i-1]到b[i]和b[i-1]到a[i]
        ans+=min( abs(a[i]-a[i-1]) + abs(b[i]-b[i-1]) , abs(a[i]-b[i-1]) + abs(b[i]-a[i-1]) );
    }
    printf("%lld\n",ans);
}

 

G - Sushi for Two

 找连续相等的部分,然后然后与上一个连续相等的部分组合,求一个max即可。

#include<bits/stdc++.h>
using namespace std;
int a[100004];
int main()
{
    int n;
    cin>>n;
    a[0]=0;
    a[n+1]=0;
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&a[i]);
    }
    int ans=0;
    int laslen=0;//上一个连续相等的部分
    int nowlen=1;//现在连续相等的部分
    for (int i = 2; i <= n+1; ++i) {
        if(a[i]==a[i-1])nowlen++;//如果现在这个与上一个一样,那么还是连续的
        else//否则不连续,进行计算
        {
            ans=max(min(nowlen,laslen),ans);//由题意
            laslen=nowlen;//更新上一个连续相等的部分
            nowlen=1;
        }
    }
    printf("%d\n",ans*2);
}

 

H - Nastya Is Buying Lunch

朴素的想法,想要将a[i]前移,首先看能否和a[i-1]交换,若不行,再看a[i-2]与a[i-1],若a[i-2]能同时与a[i-1]和a[i]交换,那么能将a[i-2]移到a[i]后面,a[i-1]和a[i]整体前移一位。

考虑普遍的情况,对于i到j,如果a[i]到a[j],都可以和a[i-1]交换,那么就可以将a[i-1]移到a[j]的后面,将a[i]到a[j]整体前移一位。

然而暴力求交集是不可行的,我们可以从v到u建单向边,用一个num[x]数组,表示x后面与x有边的点的数量,因为位移是整体前移,所以每个点对前面的贡献不会消失。

当num[x]=x到i的距离时,可以将x移到i后面,并且x不对前面的点有贡献。

整体复杂度O(n+m)    详见注释

#include <bits/stdc++.h>
using namespace std;
int a[300004];
int num[300004];//记录num[a[i]]后面的人可以和a[i]交换的人的个数
vector<int>v[300004];//v[i]维护能与i交换的点
int main (){
    int n,m;
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) {
        scanf("%d",&a[i]);
    }
    int x,y;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        v[y].push_back(x);
    }
    memset(num,0, sizeof(num));
    for (int i = 0; i < v[a[n]].size(); ++i) {//先计算n位置的贡献
        num[v[a[n]][i]]++;
    }
    int ans=0;
    for (int i = n-1; i >=1 ; --i) {
        if(num[a[i]]==n-ans-i)ans++;//整体前移一位
        else
        {
            for (int j = 0; j < v[a[i]].size(); ++j) {//否则计算该点的贡献
                num[v[a[i]][j]]++;
            }
        }
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_42671946/article/details/88752691