星星之火OIer:2018.12.15考试总结

本文深入解析了四道竞赛编程题目,包括最长不上升子序列、路径规划、奶牛慢跑和奶牛飞盘的问题解决策略。通过代码示例和算法解释,分享了如何运用贪心算法、动态规划和图搜索等技术进行高效解题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

emmm。。。

我怕是最后一个出这个的了

但还是要出一次

题目描述

第一题

第二题

第三题:路径规划

有点懒,直接给截图:

第四题:奶牛飞盘

同上


题解

第一题:题解传送门

第二题:奶牛慢跑

这道题当时在考场上完全没有思路,然后看了一下某大佬的博客以后才知道其实就是一个最长不上升子序列

我们把每头奶牛的终点算出来,然后与其起点比较一下,如果A的起点在B的起点前面,而A的终点在B的终点的后面,那就说明A一定在途中超过了B,这样就一定要到另外开一个跑道,而题目给的起点是递增的,所以就直接求一个最长不上升子序列就行了

还有一点,因为题目给的数据很大,需要nlogn的最长不上升子序列

 

那里面讲的不是很清楚,这里再推一遍

对于任意一个数列,我们用另一个数列dp[]来存这个数列的最长上不上升子序列的长度,从1~n依次扫过去,下面有两种情况:

1、a[i]小于dp里的最后一个(设为dp[len]),那么dp[++len]=a[i]

2、a[i]大于dp[len],二分求出dp中最前面的一个小于a[i]的位置,然后把这个数替换成a[i]

那么有人可能会问,这样算出来的就是正解吗?把dp里的数换了这个序列不就不存在了吗?

我们那小数据试一下

例如对(2,5,1,6,4,8,3,1)

  1. len=1,dp[]={2}
  2. 5>2,len=1,dp[]={5}
  3. 1<5,len=2,dp[]={5,1}
  4. 6>1,len=2,dp[]={6,1}
  5. 4>1,len=2,dp[]={6,4}
  6. 8>4,len=2,dp[]={8,4}
  7. 3<4,len=3,dp[]={8,4,3}
  8. 1<3,len=4,dp[]={8,4,3,1}

我们发现,从第4步开始,dp里的数列在a里已经不存在了,但是每一步的len却是对的,而答案而是4,大家可以自己算一下

而这样做的原因也很好解释

因为我们是求一个最长不上升子序列

我么就要把dp里的数尽可能的放大

这样才能给后面的数留更多的空间

然后就是代码了

代码如下::

#include<cstdio>
#include<iostream>
using namespace std;
inline void read(long long &x) {
    x=0;
    char s=getchar();
    while(s<'0'||s>'9')
        s=getchar();
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
}
inline void pr(long long x) {
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
long long a[100005],dp[100005],i,n,j,k,l=1,t;
inline int ef(long long x) {//二分求要替换的点
    int le=1,r=l+1,mid;
    while(le+1<r) {//这里要注意,一定是l+1小于r,不然会卡死
        mid=(le+r)/2;
        if(dp[mid]>=x)
            le=mid;
        else
            r=mid;
    }
    if(dp[le]<x)
        return le;
    else
        return r;
}
int main() {
    read(n),read(t);
    long long o;
    for(i=1;i<=n;i++)
        read(a[i]),read(o),a[i]=o*t+a[i];
    dp[1]=a[1];
    for(i=2;i<=n;i++)
        if(dp[l]>=a[i])
            dp[++l]=a[i];
        else
            k=ef(a[i]),dp[k]=a[i],l=max(l,k);//没什么好解释的吧,还是很简单
    pr(l);
}

第三题:路径规划

这道题看起来很难,其实想通就很好了,只要把这两个人到每个点所用的最短距离计算出来(dis1,dis2),再把每个点到n的距离计算出来(dis3),然后枚举每一个相遇的点,总花费就是dis1*B+dis2*E+dis3*P,然后求个最小值就好了

代码如下::

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
using namespace std;
inline void read(int &x) {
    x=0;
    int f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
vector<int>G[40005];//存图
int dis1[40005],dis2[40005],dis3[40005],i,n,j,k,a,b,B,E,P,m,vis[40005],ans=2147483647;
inline void bfs(int s,int dis[]) {//用bfs求出1,2,n到每个点的距离
                                  //其实为什么这个数组不用定义为实参而能改变值我不是很清楚
                                  //求解答
    queue<int>q;//在里面定义,不然要清零,浪费时间
    q.push(s);
    while(!q.empty()) {
        int t=q.front();
        q.pop();
        vis[t]=1;
        int l=G[t].size();
        for(i=0;i<l;i++) {//每个点依次搜
            if(vis[G[t][i]]==0) {//第一次搜到就一定是最短的
                dis[G[t][i]]=dis[t]+1;
                vis[G[t][i]]=1;
                q.push(G[t][i]);
            }
        }
    }
}
int main() {
    read(B),read(E),read(P),read(n),read(m);
    for(i=1;i<=m;i++)
        read(a),read(b),G[a].push_back(b),G[b].push_back(a);
    bfs(1,dis1);
    memset(vis,0,sizeof(vis));
    bfs(2,dis2);
    memset(vis,0,sizeof(vis));//memset可以写在bfs里面,这样写只是为了节省一点时间
    bfs(n,dis3);
    for(i=1;i<=n;i++)
        ans=min(ans,dis1[i]*B+dis2[i]*E+dis3[i]*P);//计算最小值
    pr(ans);
}

第四题:奶牛飞盘

这道题的数据很小,乍一看可以用暴搜,但20!=2432902008176640000,肯定要超时,所以在加一点贪心的因素

比如现在已经有了一些奶牛,现在要放 i 和 j 两头奶牛,设现在这堆奶牛还能承受W的重量,那么我们来推一遍

1、i->j,W=min(W - 重量[i]-重量[j],承受[i]-重量[j])

2、j->i,W=min(W - 重量[j]-重量[i],承受[j]-重量[i])

那么设先放i优于j,则有承受[i]-重量[j]>承受[j]-重量[i]

移项,得承受[i]+重量[i]>承受[j]+重量[j]

这就是贪心排序顺序了

最后dfs依次搜一遍就可以得出正解

代码如下::

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
inline void read(long long &x) {
    x=0;
    long long f=1;
    char s=getchar();
    while(s<'0'||s>'9') {
        if(s=='-')
            f=-1;
        s=getchar();
    }
    while(s>='0'&&s<='9') {
        x=x*10+s-48;
        s=getchar();
    }
    x*=f;
}
inline void pr(long long x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9)
        pr(x/10);
    putchar(x%10+48);
}//快读快输不解释
struct node {
    long long hi/*高度high*/,he/*重量heavy*/,st/*强壮strong*/;
}a[25];
long long i,n,j,k,H,ans=-1;
bool vis[25]={0,1};
inline bool cmp(node a,node b) {
    return a.he+a.st>b.he+b.st;//按退出来的式子来排序
}
void dfs(long long x,long long s,long long h) {//分别表示第x头牛,还能承受的重量,现在的高度
    if(h>=H) {//已经到达了飞盘的高度
        ans=max(ans,s);//如果再向上加奶牛,承受值一定会减小,所以挡墙决策一定在局部最优
        return;
    }
    if(x>n)//所有牛都用完了
        return;
    if(s>a[x].he) {//现在的承受量比当前奶牛的重量大,可以继续放
        vis[x]=1;//
        dfs(x+1,min(s-a[x].he,a[x].st),h+a[x].hi);//回溯操作
        vis[x]=0;
    }
    dfs(x+1,s,h);//可以选择不放当前奶牛
}
int main() {
    read(n),read(H);
    for(i=1;i<=n;i++)
        read(a[i].hi),read(a[i].he),read(a[i].st);
    sort(a+1,a+1+n,cmp);//排序
    dfs(1,9223372036854775807,0);//一定要从第1头开始,因为有可能不放第1头
    if(ans==-1)//够不到
        printf("Mark is too tall");
    else
        pr(ans);
}

总结

这次考试考得不是很理想,因为在第1题上浪费了太多的时间,而最后一题的暴搜也没有打对

另外两道题一道随缘,一道输样例

还是要在这方面努力些

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值