有一些题目我做不来,暂时也没搜到令人满意的答案,欢迎评论区补充呀
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);
}