复旦机试题2020

博主分享了使用三层回溯法解决计数问题,运用动规优化数组操作,忽略了边界条件导致的时间消耗,以及在一维数组中实现树结构的挑战。最后反思了动规思想在数学问题中的应用。重点回顾了树、vector和数据结构的最佳实践。
摘要由CSDN通过智能技术生成

前言:

题目来源于这里

第一题:回溯法是什么?我用三层for循环写的,看了别的博主的,似乎因为数组小而并没有爆掉

第二题:我用的动规,并且不会用vector

#include <iostream>
#include<stdio.h>
#include<algorithm>

using namespace std;

int a[100010];
int dp[10010]={};

int main()
{
    int n,d,max=0;
    scanf("%d%d",&n,&d);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    sort(a,a+n);
    //dp[0]=1;
    for(int i=1;i<n;i++){
        for(int j=0;j<i;j++){
            if((a[i]-a[j]>=d||a[i]-a[j]<=-d)&&dp[i]<dp[j]+1){
                dp[i]=dp[j]+1;
            }
        }
    }
    for(int i=0;i<n;i++){
        if(max<dp[i]){
            max=dp[i];
        }
    }
    if(max==0){
        printf("0\n");
    }
    else{
        printf("%d",max+1);//如果存在的话,还要算上第一个数
    }
    return 0;
}

第三题:我没考虑到后一个同学到达时无人在排队的边界情况(前三共耗时59min)

#include <iostream>
#include<stdio.h>
#include<algorithm>

using namespace std;

struct Student{
    long a;//到达时间
    long t;//打饭耗时
    long b;//等待时间上限
    long start;//开始打饭时间
    long leave;//离开时间
    int flag=0;//没有离开
}student[100010];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%ld%ld%ld",&student[i].a,&student[i].t,&student[i].b);
    }
    if(n>0){     //至少有一个人来吃饭了
        student[0].start=student[0].a;
        student[0].leave=student[0].start+student[0].t;//离开时间=开始时间+打饭耗时
    }
    for(int i=1;i<n;i++){
        if(student[i].a+student[i].b>=student[i-1].leave){//若i同学等待上限时间点不小于i-1同学结束打饭时间点
            student[i].start=student[i-1].leave;
            student[i].leave=student[i].start+student[i].t;//离开时间=开始时间+打饭耗时
        }
        else{  //提前离开了
            student[i].leave=student[i-1].leave;
            student[i].flag=1;//离开了的标记
        }
    }
    for(int i=0;i<n;i++){
        if(student[i].flag!=1){//没有离开
            printf("%ld",student[i].start);
        }
        else{
            printf("-1");
        }
        if(i<n-1) printf(" ");

    }
    return 0;
}

第四题:晕晕,因为我忘记树怎么写了,只能用一维数组存树,空间复杂度o(2^n)我去!!(做完1、2、3、4共用时2h2min)

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>

using namespace std;

int tree[500010]={};
int sum=0;
int n;//A的长度

void visit(int i){
    if(tree[2*i]!=0){//有左孩子
        visit(2*i);
    }
    if(tree[i]!=0){
        if(i==1){
            printf("0");
        }
        else{
            printf("%d",tree[i/2]);
        }
    }
    sum++;
    if(sum<n) printf(" ");
    if(tree[2*i+1]!=0){
        visit(2*i+1);
    }
}

int main()
{
    scanf("%d",&n);
    if(n>0) scanf("%d",&tree[1]);
    for(int i=2;i<=n;i++){
        int temp;
        scanf("%d",&temp);
        for(int j=1;j<pow(2,n);){
            if(temp>tree[j]){
                if(tree[2*j+1]==0){//有空位
                    tree[2*j+1]=temp;
                    break;
                }
                else{
                    j=2*j+1;
                }
            }
            else{
                if(tree[2*j]==0){//有空位
                    tree[2*j]=temp;
                    break;
                }
                else{
                    j=2*j;
                }
            }
        }
    }
    visit(1);
    return 0;
}

第五题:光想就想了好久,我没意识到这个是动规,自己按数学题做了,答案好像是对的,abs()是求绝对值(共耗时3h8min)

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>

using namespace std;

int a[100010];
int b[100010];
int dp[100010]={};//以i结尾的元素前面有多少个连续大于ave_A取下界的元素
int dp_change[100010]={};//记录b数组中哪些元素改变过

int main()
{
    int n;//A的长度
    int sum_A=0,quanzhi=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        sum_A+=a[i];
    }
    int ave_A=sum_A/n;
    for(int i=0;i<n;i++){
        b[i]=ave_A;
        if(a[i]>ave_A){
            dp[i]=dp[i-1]+1;
        }
    }
    for(int i=0;i<n;i++){
        if(dp[i]==2){//存在连续两个大于平均值下界的时候,b[j]++,平方项权值最多只会增加2,而绝对值权值会减小>=2,能使整体权值减小
            int j;
            for(j=i-1;dp[j]>0&&j<n;j++){
                b[j]++;
                dp_change[j]=1;
            }
            i=j+1;
        }
    }
    //边界大于平均值下界而倒数第二个不大于时,其本身权值变动可抵消,但是可以规避大-小-大,平方项权值+2的问题
    //末尾边界
    if(dp[n-2]==0&&dp[n-1]==1){
        b[n-1]++;
        dp_change[n-1]=1;
    }
    //起始边界
    if(dp[1]==0&&dp[0]==1){
        b[0]++;
        dp_change[0]=1;
    }

    for(int i=1;i<n-1;i++){//出现大-小(i)-大情况时,将b[i]加一可以使平方项权值-2,而绝对值项权值只会+1,使总体权值-1
        if(dp_change[i]==0&&dp_change[i-1]==1&&dp_change[i+1]==1){
            b[i]++;
            dp_change[i]=1;
            i++;
        }
    }

    for(int i=0;i<n;i++){
        if(b[i]-a[i]>0){
            quanzhi=quanzhi+b[i]-a[i];
        }
        else{
            quanzhi=quanzhi-b[i]+a[i];

        }
    }
    for(int i=0;i<n-1;i++){
        if(b[i]!=b[i+1]){
            quanzhi++;//因为b数组的值只有正负1的波动
        }
        //quanzhi=quanzhi+pow(b[i]-b[i+1],2);
    }
    printf("%d\n",quanzhi);
    printf("he");
    return 0;
}

总结:

  1. 复习树怎么写
  2. 复习文件输入输出
  3. 复习vector等stl的写法(vector最常用)
  4. 复习动规
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值