THU2020年考研机试题解

14 篇文章 0 订阅
14 篇文章 1 订阅

有一些题目我做不来,暂时也没搜到令人满意的答案,欢迎评论区补充呀

2020

1.统计次数

时间限制: 1.0 秒

空间限制: 512 MB

题目描述
给定两个正整数 n 和 k(1≤k≤9),求从 1 到 n 这 n 个正整数的十进制表示中 k 出现的次数。

输入格式
从标准输入读入数据。

输入的第一行包含两个正整数 n 和 k,保证 n≤106 和 1≤k≤9。

输出格式
输出到标准输出。

输出一个整数,表示答案。

样例1输入
12 1
样例1输出
5

1.1题目来源

洛谷:https://www.luogu.com.cn/problem/P1980
这是道比较简单的,还有一种比较困难的超大整数的版本,超大整数的版本用下面这种思路解决:
我用一个小一点的数来说明一下思路,拿50906来举个例子
比如我们要求1~50936之间,数字3出现的次数

  • 先判断3在最高位万位出现的次数,出现的数字为30000~39999,一共1×10000次
  • 判断3在千位出现的次数,由于0<3,所以出现的次数为3000~3999,…,493000~493999,一共50×1000
  • 判断3在百位可能出现的次数,由于9>3,出现的数字为300~399,1300~1399,…,50300~50399,一共是(50+1)×100次
  • 判断3在十位出现的次数,出现的数字为30~39,130~139,…,49930~49939,50930~50936,一共是500×10+7次
  • 判断3在个位数出现的次数,出现的数字为3,13,23,…,50933,一共是(50933+1)×1次
    总结一下就是,记cur为当前数位上的数,k为所求的数字,cnt为所求的总数
    那么:
  • cur>k时,cnt+=(高位的数值+1)*当前数位代表的倍数
  • cur==k时,cnt+=(高位的数值)*当前数位代表的倍数+低位数值+1
  • cut<k时,cnt+=高位的数值*当前数位代表的倍数

1.2 AC代码

#include<stdio.h>
int main(){
    int n,x;
    int cnt=0;
    int a=0,b=0;
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;i++){
        b=i;
        do{
            a=b%10;
            if(a==x)
                cnt++;
            b=b/10;
        }
        while(b);

    }
    printf("%d",cnt);
}

2.等差数列

时间限制: 1.0 秒

空间限制: 512 MB

题目描述
有一个特殊的 n 行 m 列的矩阵 Aij(1≤i≤n,1≤j≤m),每个元素都是正整数,每一行和每一列都是独立的等差数列。在某一次故障中,这个矩阵的某些元素的真实值丢失了,被重置为 0。现在需要你想办法恢复这些元素,并且按照行号和列号从小到大的顺序(行号为第一关键字,列号为第二关键字,从小到大)输出能够恢复的元素。

输入格式
从标准输入读入数据。

输入的第一行包含两个正整数 n 和 m,保证 n≤103 和 m≤103。

接下来 n 行,每行 m 个整数,表示整个矩阵,保证 1≤Aij≤109。如果 Aij 等于 0,表示真实值丢失的元素。

输出格式
输出到标准输出。

输出若干行,表示所有能够恢复的元素。每行三个整数 i,j,x,表示 Aij 的真实值是 x。

样例1输入
3 4
1 2 0 0
0 0 0 0
3 0 0 0
样例1输出
1 3 3
1 4 4
2 1 2
样例1解释
可以恢复 3 个元素, A13 的真实值是 3,A14 的真实值是 4,A21 的真实值是 2。

2.1题目来源

找不到题目来源,也没找着相关的OJ,不过阿里2020.3.25有一道笔试题倒是和这道题很像

给出一个二维矩阵,这个矩阵的每一行和每一列都是一个独立的等差数列,其中一些数据缺失了,现在需要推理隐藏但是可以被唯一确定的数字,然后对输入的查询进行回答。

输入描述:
第一行,n,m,q分别表示矩阵的行数,列数和查询的条数。
接下来的n行,每行m个数表示这个矩阵,0表示缺失数据。
 
接下来q行,每行两个数字i,j表示对矩阵中第i行第j列的数字进行查询。

输出描述:
如果可以确定该位置的数字,则输出数字,如果不能确定则输出UNKNOWN。

输入示例:
2 3 6
1 0 3
0 0 0
1 1
1 2
1 3
2 1
2 2
2 3

输出示例:
1
2
3
Unknown
Unknown
Unknown

这道题我不是很会做,因为对于一个位置的数值而言,可以通过以下两种方法推导获得:

  • 通过输入的信息一次推导即可得到该位置的值,这种情况很简单,我后面的代码也是按这个思路来写的
  • 而有的位置的数据,一次推导不行,但是可以通过一次推导得到其它位置的数据,然后通过这些新数据进行后续的推导,这种情况我就不是很会做
    根据N诺上的评测,本题应该是用按第一种情况来做的

2.2 代码(待完善)

就是逐行遍历一次,求出每一行的公差;然后逐列遍历,求出每一列的公差,最后根据这些公差再逐个位置补数据

有两个注意点,也是我暂时想不通的点:

  • 不要根据上一个数求出当前的数,比如不要根据a[i][j-1]求出a[i][j],这样会过不了N诺的评测
  • 需要根据每一行/列上第一个不为0的数的位置来求当前当前的数,不然也过不了N诺的评测
//我就做初次可以推断的吧
#include <stdio.h>
#define maxn 1005
#define maxm 1005

int array[maxn][maxm];//存储输入的矩阵

typedef struct Node2{
    int cnt;//存储这一行,或者这一列的不为0的个数
    int delta;//存储这一行,或者这一列的公差
    int loc;//存储这一行/列第一个不为0的数的位置;
    // 注意,一定是第一个不为0的数的位置,如果是其它数的位置会WA,至于为什么我还不清楚
}node2;

//heng数组存的是每一行的信息
//shu数组存的是每一列的信息
node2 heng[maxn],shu[maxm];

int n,m;

const int INF=1000000000;

int main(){

    scanf("%d%d",&n,&m);
    int i,j;
    for(j=0;j<m;j++){
        shu[j].cnt=0;
        shu[j].delta=INF;
    }

    for(i=0;i<n;i++){
        heng[i].cnt=0;
        heng[i].delta=INF;
        for(j=0;j<m;j++){
            scanf("%d",&array[i][j]);
            if(array[i][j]!=0){
                heng[i].cnt++;
                shu[j].cnt++;
            }
        }
    }
    int a=0,ai=0,aj=0;
    int b=0,bi=0,bj=0;

    //求每一行的公差
    for(i=0;i<n;i++){
        a=0,ai=0;
        if(heng[i].cnt<=1)
            continue;
        for(j=0;j<m;j++){
            if(array[i][j]==0)
                continue;
            if(a==0){
                a=array[i][j];
                ai=j;
                heng[i].loc=j;
            }
            else{
                b=array[i][j];
                bi=j;
                heng[i].delta=(b-a)/(bi-ai);
                break;
            }
        }
    }

    //求每一列的公差
    for(j=0;j<m;j++){
        a=0,aj=0;
        if(shu[j].cnt<=1)
            continue;
        for(i=0;i<n;i++){
            if(array[i][j]==0){
                continue;
            }
            if(a==0){
                a=array[i][j];
                aj=i;
                shu[j].loc=i;
            } else{
                b=array[i][j];
                bj=i;
                shu[j].delta=(b-a)/(bj-aj);
                break;
            }
        }
    }

    //补数据
    for(i=0;i<n;i++){
        for(j=0;j<m;j++){
            if(array[i][j]){
                continue;
            }
            if(heng[i].delta!=INF){
                array[i][j]=array[i][heng[i].loc]+heng[i].delta*(j-heng[i].loc);
                printf("%d %d %d\n",i+1,j+1,array[i][j]);

            }
            else if(shu[j].delta!=INF){
                array[i][j]=array[shu[j].loc][j]+shu[j].delta*(i-shu[j].loc);
                printf("%d %d %d\n",i+1,j+1,array[i][j]);
            }
        }
    }
}

3.图(蹲一位大佬)

时间限制: 1.0 秒

空间限制: 512 MB

题目描述
给定一个有 n 个点,m 条边的有向图。图中第 i 个点的价值是 vi,每条边有一个代价 z,不同的边代价可能不一样。

一共有 q 个询问,每次询问包含两个数字 u,c,表示询问从 u 点出发,经过代价总和不超过 c 的边所能到达的点的价值总和的最大值。

如果一个点被多次经过,那么其价值要计算多次。初始节点的价值也要计算进去。

输入格式
从标准输入读入数据。

输入的第一行包含三个由空格隔开的正整数 n,m,q,保证 N≤2,000 和 M≤8,000,Q≤105。

接下来的一行包括 n 个由空格隔开的非负整数 vi 表示编号从小到大所有点的价值,保证 vi≤104。

接下来的 m 行每行包含三个由空格隔开的正整数 x,y,z,保证 1≤x,y≤n 和 1≤z≤30,表示存在一条从 x 到 y 代价为 z 的有向边。

接下来的 q 行每行包含两个由空格隔开的非负整数 u,c,保证 1≤u≤n 和 0≤c≤800。

输出格式
输出到标准输出。

对于每次询问输出一个数,表示相应的答案。

样例1输入
4 4 2
3 2 3 4
1 2 1
2 3 1
3 2 2
3 4 1
2 6
3 2
样例1输出
14
7
样例1解释
对于第一个询问最优方案是从 2 出发,经过 3,2,3,4 四个点,取得的价值是 2+3+2+3+4=14。

对于第二个询问最优方案是从 3 出发,经过 4 这个点,取得的价值是 3+4=7。

子任务
对于所有子任务,保证:n≤2,000,m≤8,000,c≤800,q≤105。

对于第一个子任务(17 points),保证:n≤5,m≤10,c≤8,q≤8
对于第二个子任务(23 points),保证:u=1
对于第三个子任务(25 points),保证:q≤5。

对于第四个子任务(35 pints),保证:n≤2,000,m≤8,000,c≤800,q≤105。

3.1 代码

就是深度遍历

#include <bits/stdc++.h>
#define maxn 2005

int v[maxn];
int mp[maxn][maxn];
int result;

int n,m,q;
int u,limit;

void dfs(int beg,int limit,int value){
    if(value>result)
    	//这个result是个全局变量,每一条路径进来的时候,都会进行一次比较
    	//如果某条路径上的value更大,就用这条路径上的value
        result=value;
    for(int i=1;i<=n;i++){
        if(mp[beg][i]<=limit)
        	//对于每一条路径,都试着进去跑一遍,经过limit-mp[beg][i]后,
        	//每一条路径的初始上限就各不相同了
            dfs(i,limit-mp[beg][i],value+v[i]);
    }
}

int x,y,z;
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&v[i]);
    }

    memset(mp,0x3f,sizeof mp);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        mp[x][y]=z;
    }

    while(q--){
        scanf("%d%d",&u,&limit);
        result=0;
        dfs(u,limit,v[u]);
        printf("%d\n",result);
    }
}

2019

T3.论文

题目描述
Time Limit: 1000 ms
Memory Limit: 256 mb
小H为了完成一篇论文,一共要完成n个实验。其中第i个实验需要ai的时间去完成。

小H可以同时进行若干实验,但存在一些实验,只有当它的若干前置实验完成时,才能开始进行该实验。

同时我们认为小H在一个实验的前置实验都完成时,就能马上开始该实验。

为了让小H尽快完成论文,需要知道在最优的情况下,最后一个完成的实验什么时候完成?

小H还想知道,在保证最后一个实验尽快完成的情况下(即保证上一问的答案不变),他想知道每个实验最晚可以什么时候开始。

设第i个实验最早可能的开始时间为fi,不影响最后一个实验完成时间的最晚开始时间为gi,请你回答

在这里插入图片描述

除以10^9+7所得的余数。

题目保证有解。

输入输出格式
输入描述:
从标准输入读入数据。
第一行输入一个整数n,m。
第二行输入n个正整数,a1,a2,…an,描述每个实验完成所需要的时间。
接下来读入m行,每行读入两个整数u,v,表示编号为u的实验是编号为v的实验的前置实验。
对于所有的输入数据,都满足1<=n<=105,1<=m<=5*105,1<=ai<=10^6。
输出描述:
输出到标准输出。
第一行输出一个整数表示最晚完成的实验的时间。
第二行输出一个整数表示除以10^9+7所得的余数。
输入输出样例
输入样例:
7 5
11 20 17 10 11 17 17
5 4
6 1
7 3
2 4
2 1
输出样例:
34
7840
提示
第一个点最早开始时间为20,最晚开始时间为23。
第二个点最早开始时间为0,最晚开始时间为3。
第三个点最早开始时间为17,最晚开始时间为17。
第四个点最早开始时间为20,最晚开始时间为24。
第五个点最早开始时间为0,最晚开始时间为13。
第六个点最早开始时间为0,最晚开始时间为6。
第七个点最早开始时间为0,最晚开始时间为0。

代码

最早开始时间和最晚开始时间的模板题,我用的是dfs

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

int n,m,v,u;

#define N 10005
int MOD=1000000007;

vector<int> edge[N];

int f[N];
int ans;
int t[N];

vector<int> edge_son[N];//存储儿子
int l[N];//存储最晚结束时间

int dfs(int x){
    if(f[x]) return f[x];
    for(int i=0;i<edge[x].size();i++){
        f[x]=max(f[x],dfs(edge[x][i]));
    }
    f[x]+=t[x];
    return f[x];
}

int dfs_min(int x,int ans){
    if(l[x]<ans) return l[x];
    for(int i=0;i<edge_son[x].size();i++){
        l[x]=min(l[x],dfs_min(edge_son[x][i],ans)-t[edge_son[x][i]]);
    }
    return l[x];
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&t[i]);
    for(int i=0;i<m;i++){
        scanf("%d%d",&u,&v);
        edge_son[u].push_back(v);
        edge[v].push_back(u);

    }

    //找最早结束时间
    for(int i=1;i<=n;i++){
        ans=max(ans,dfs(i));
    }

    //先初始化l数组
    for(int i=1;i<=n;i++)
        l[i]=ans;

//    找最晚结束时间
    for(int i=1;i<=n;i++){
        dfs_min(i,ans);
    }

    int sum=1;
    for(int i=1;i<=n;i++){
        sum=sum*((l[i]-f[i]+1)%MOD)%MOD;
    }

    printf("%d\n%d",ans,sum);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值