HRBU 2021暑期训练解题报告阶段四Day3

目录

A - Cow Acrobats

B - Dropping tests

C - K Best

D - Pyramid Split

E - Alignment

F - Longest Ordered Subsequence

G - 排序


A - Cow Acrobats

题意:

给n只奶牛,每只奶牛都有两个属性, 风险值(w[i])和抗压值(s[i])。
现在要把这些牛垒在一起, 每个牛都有一个危险值:位他上面的牛的风险值之和减去它的抗压值。
问最大危险值的最小值?
(n<=5e4,1 <= w[i]<= 1e4,1 <= s[i] <= 1,000,000,000 )

思路:

看似二分,但是没有好下手的点,但是可以用二分。

我们假设现在n头牛已经叠好罗汉,从上往下第i头牛与第(i+1)头牛的数据为:

(sum:前i-1头牛的风险值之和)

属性\牛 第i头牛 第i+1头牛
风险值 w[i] w[i+1]
抗压值 s[i] s[i+1]
危险值 (C1)sum-s[i] (C2)sum+w[i]-s[i+1]

现在我们交换两者的位置,交换位置之后只会对这两头牛产生影响:

属性\牛 第i头牛 第i+1头牛
风险值 w[i] w[i+1]
抗压值 s[i] s[i+1]
危险值 (C3)sum+w[i+1]-s[i] (C4)sum-s[i+1]

此时C3>C1,C4<C2。

我们对比C2与C3的值:

C2-C3== w[i]-s[i+1]-w[i+1]+s[i] == w[i]+s[i]-(w[i+1]+s[i+1])

此时我们发现,影响危险值变换的是w+s的值,所以我们进行贪心,按照w+s的值由小到大排序。

  • 考察点:贪心,思维,递推,二分

代码:

///¿¨ÊäÈë
#include<algorithm>
#include<cstdio>
#include<iostream>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
struct node
{
    ll weight,power;
}a[maxn];

bool cmp(node x,node y)
{
    return x.power+x.weight<y.power+y.weight;
}
int main()
{
    int n;
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].weight>>a[i].power;
    sort(a+1,a+1+n,cmp);
//    for(int i=1;i<=n;i++)
//        cout<<a[i].weight<<" "<<a[i].power<<endl;
    ll ans=-inf,sum=0;
    for(int i=1;i<=n;i++){
        //cout<<sum<<" "<<a[i].power<<endl;
        ans=max(sum-a[i].power,ans);
        sum+=a[i].weight;
    }
    cout<<ans<<endl;
}

B - Dropping tests

题意:

给出长度为n的序列a、b,ai与bi为绑定关系。
现在你可以去掉k组(ai,bi),使得题目中的公式得到一个最大值,输出这个最大值。

思路:

二分答案的经典问题之一:最大化平均值

我们转化一下公式:

 接下来我们二分m的值即可。

  • 考察点:二分

代码:

#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
struct node
{
    double a,b;
}arr[1050];

double num[1050];
int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        if(n==0&&k==0)
            break;
        for(int i=1;i<=n;i++){
            scanf("%lf",&arr[i].a);
        }
        for(int i=1;i<=n;i++){
            scanf("%lf",&arr[i].b);
        }
        double l=0.0,r=1.0;
        double mid;
        while(r-l>1e-8){
            mid=(l+r)/2*1.0;
            for(int i=1;i<=n;i++)
                num[i]=arr[i].a-mid*arr[i].b;
            sort(num+1,num+1+n);
            double sum=0;
            for(int i=k+1;i<=n;i++){
                sum+=num[i];
            }
            //printf("%f\n",sum);
            if(sum>0)
                l=mid;
            else
                r=mid;
        }
        printf("%.0f\n",mid*100);
    }
    return 0;
}

C - K Best

题意:

现在有n个珍珠,每个珍珠都有一个价值vi与重量wi。
我们把  价值和/重量和 称为珍珠们的性价比。
我现在要买k个珍珠,请问我能拿到的最大性价比是多少?

思路:

跟B题一样,最大化平均值。

还是先化简公式:

 接下来还是二分m的值。

  •  考察点:二分

代码:

///输入要用scanf/快读
#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
const int maxn=1e5+100;
struct node
{
    int pos;
    double p,v,w;
}a[maxn];
bool cmp(node x,node y)
{
    return x.p>y.p;
}
int b[maxn];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        a[i].pos=i;
        scanf("%lf%lf",&a[i].v,&a[i].w);
    }
    double l=0,r=10000010;
    while((r-l)>1e-8)
    {
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++)
            a[i].p=a[i].v-a[i].w*mid;
        sort(a+1,a+1+n,cmp);
        double sum=0;
        for(int i=1;i<=k;i++){
            sum+=a[i].p;
            b[i]=a[i].pos;
        }

        if(sum<0)
            r=mid;
        else
            l=mid;
    }
    for(int i=1;i<=k;i++){
        if(i!=1) printf(" ");
        printf("%d",b[i]);
    }
    printf("\n");
}

D - Pyramid Split

题意:

给你n个四棱锥,一起放在一个平面上。
然后在高度为h的位置,用一个平行平面去切所有的四棱锥,得到上下两部分。
使上下两部分体积相等,求h的大小(取整数部分输出)。

思路:

首先我们要知道四棱锥的体积公式

接下来我们二分这个h的值。

假设当前的高为h,算出每个四棱锥切割之后上半部分的体积和v,与总体积V进行比较。

1.v>V ==>  h过小

2.v<V ==>  h过大

3.v==V ==> h合适

  • 考察点:二分,数学

代码:

#include <algorithm>
#include <cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;

int a[maxn],b[maxn];
double sum;
int n;
bool check(double x)
{
    double ans=0;
    for(int i=1;i<=n;i++)
    {
        if(x>=a[i]) continue;
        double bili=(double(a[i])-x)/a[i];
        ans+=b[i]*b[i]*bili*bili*(double(a[i])-x);
    }
    if(ans>=sum)
        return true;
    else
        return false;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {

        sum=0;
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        for(int i=1;i<=n;i++)
        {
            cin>>b[i];
            sum+=b[i]*b[i]*a[i]*1.0;
        }
        sum/=2;
        double l=0,r=10000,mid;
        while(r-l>1e-8){
            mid=(l+r)/2;
            if(check(mid))
                l=mid;
            else
                r=mid;
        }
        cout<<(int)mid<<endl;
    }
}

E - Alignment

题意:

现在有长为n的序列a。
求当处理之后的序列满足a1 < a2 < a3 < ... < a(i ) <=> a(i+1) > a(i+2) > .. a(n-1) >a(n)
请问最少要删除的数字个数。

思路:

P1091 [NOIP2004 提高组] 合唱队形基本上是同一题了。

差距只在于一点:

  • P1091中前后严格递增递减:1 2 3 4 3 2 1
  • 这道题并不是严格递增递减的 :1 2 3 4 4 3 2 1

所以这题在算答案的时候稍有不同。

  • 考察点:LIS,思维,动态规划

代码:

#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
double a[1005];
int lis1[1005],lis2[1005];
int main()
{
    int n;
    ios::sync_with_stdio(false);
    while(cin>>n)
    {
        int ans=-1;
        for(int i=1; i<=n; i++)
            cin>>a[i];
        ///第一遍LIS,正序
        for(int i=1; i<=n; i++)
        {
            lis1[i]=1;
            for(int j=1; j<i; j++)
                if(a[i]>a[j])
                    lis1[i]=max(lis1[i],lis1[j]+1);
        }

        ///第二遍LIS,逆序
        for(int i=n; i>=1; i--)
        {
            lis2[i]=1;
            for(int j=n; j>i; j--)
                if(a[i]>a[j])
                    lis2[i]=max(lis2[i],lis2[j]+1);
        }
        for(int i=1; i<=n; i++)
            for(int j=i+1;j<=n;j++)
            ans=max(ans,lis1[i]+lis2[j]);

        ///注意!!!!我们求得的是K,我们要的是n-k!!!
        cout<<n-ans<<endl;
    }
    return 0;
}

F - Longest Ordered Subsequence

题意:

给出长度为n的数字序列a
输出它的LIS长度。

思路:

LIS(Longest Increasing Subsequence,最长上升子序列)模板题。

  • 考察点:动态规划,二分,LIS

代码:

#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int a[1050];
int lis[1050];
int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            cin>>a[i];
        lis[1]=a[1];
        int len=1;
        for(int i=2;i<=n;i++){
            if(a[i]>lis[len])
                lis[++len]=a[i];
            else{
                int pos=lower_bound(lis+1,lis+1+len,a[i])-lis;
                lis[pos]=a[i];
            }
        }
        cout<<len<<endl;
    }
}

G - 排序

题意:

中题你我?

思路:

没啥好说的,坑点有点多而已。

  • 考察点:字符串,思维

  • 坑点:两个5之间没有数字 、末尾没有5

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
ll a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    string ss;
    while(cin>>ss)
    {
        if(ss[ss.size()-1]!='5')
            ss+='5';
        int cnt=0,len=0;
        ll ans=0;
        for(int i=0; i<ss.size(); i++)
        {
            if(ss[i]=='5')
            {
                if(len)
                {
                    a[cnt++]=ans;
                    ans=0;
                    len=0;
                }
            }
            else
            {
                len++;
                ans=ans*10+ss[i]-'0';
            }

        }
        sort(a,a+cnt);
        for(int i=0; i<cnt; i++)
        {
            if(i)
                cout<<" ";
            cout<<a[i];
        }
        cout<<endl;
    }
    return 0;
}

 

おすすめ

転載: blog.csdn.net/qq_45750296/article/details/119917507