CCF考试系统传送门
2023.03第29次CCF-CSP计算机认证考试
CCF计算机软件能力认证考试系统
前言
最近真忙,五一可能也没时间了,之后的解题分析说不定就鸽了(悲)
一、现值计算
1.题目内容
2.题解分析
按题目给的公式来全部转换为第0年的价值,然后累加和。其中输入的顺序 j正好对应年份k。
解题关键公式:
3.完整代码
#include<bits/stdc++.h>
using namespace std;
const int MAX_N=60;
double n,i;
double money,ans=0.0;
int main()
{
scanf("%lf%lf",&n,&i);
for(int j=0;j<=n;j++)
{
scanf("%lf",&money);
ans+=money*pow(1.0+i,(double)-j);
}
printf("%.3lf",ans);
return 0;
}
评测结果
二、训练计划
1.题目内容
2.题解分析
写完之后,发现老师的留言:
再看了一眼关键路径,好吧,这应该接近关键路径算法的板子题了。
先留个位置,有时间补个关键路径做法:
关键路径
…
…
再说说我的做法。
数据结构:并查集
用了点并查集的思想。因为数据量小,所以可以用比较暴力的方法。
关键利用到并查集的寻根操作,我们可以将查找时经过的路径对应上一个权值(即训练时间)查找时将所有权值加起来,即可以得出最早时间和最晚时间。
最早时间比较简单,因为是并查集的建立方向
int caltime1(int x)
{
int sum;
sum=t[s[x]];//加下个结点的值
return s[x]==x?sum:sum+caltime1(s[x]);
}
//在solve函数中调用:
if(model==1)
{
day=caltime1(i)+1;
if(day+t[i]-1>n)
cmplt=0;//完成标记
}
求最晚时间时,因为逆着并查集建立方向,比较麻烦。对于求最晚时间的点,找其他哪个点的父亲/祖宗是它,如果是,求从那个点到待求点的路径权值和即可,然后维护一个最大的权值(即为最晚开始时间)
bool findroot(int x,int dest)//dest是否是x的父亲/祖宗
{
if(x==dest)
return 1;
else if(s[x]==x)//为共同根0,则不为
return 0;
else return findroot(s[x],dest);
}
int caltime2(int x,int dest)
{
int sum;
sum=t[x];//加上当前结点值
return x==dest?sum:sum+caltime2(s[x],dest);
}
//在solve函数中调用:
else
{
for(int j=m;j>=i;j--)
{
if(findroot(j,i))//j的父亲/祖宗是i
day=max(day,caltime2(j,i));
}
day=n-day+1;
}
最复杂情况下复杂度为O( n 2 n^2 n2)
(之前写的,语无伦次的分析 )
这道题的核心,我们可以这样理解:先将每个点按关系连接,然后在每个关系中取时间线最长的作为主干,其他是分支。对于某个点,若它在主干上,最早开始时间与它的前缀和(不包括该点)有关系,最晚开始时间和它的后缀和(包括该点)有关系;若在分支上,则需要先找到分支直接相连的主干点,这条整条分支上的点的最早/晚开始时间都与这个主干点有关。
图解:
3.完整代码
#include<bits/stdc++.h>
using namespace std;
const int MAX_M=110;
int n,m,p;
bool cmplt=1;//默认可以完成
int s[MAX_M],t[MAX_M];
bool findroot(int x,int dest)
{
if(x==dest)
return 1;
else if(s[x]==x)//为共同根0,则不为前缀
return 0;
else return findroot(s[x],dest);
}
int caltime1(int x)
{
int sum;
sum=t[s[x]];//加下个结点的值
return s[x]==x?sum:sum+caltime1(s[x]);
}
int caltime2(int x,int dest)
{
int sum;
sum=t[x];//加上当前结点值
return x==dest?sum:sum+caltime2(s[x],dest);
}
void solve(int model)
{
for(int i=1;i<=m;i++)
{
int day=0;
if(model==1)
{
day=caltime1(i)+1;
if(day+t[i]-1>n)
cmplt=0;
}
else
{
for(int j=m;j>=i;j--)
{
if(findroot(j,i))//j的父亲/祖宗是i
day=max(day,caltime2(j,i));
}
day=n-day+1;
}
printf("%d ",day);
}
}
int main()
{
scanf("%d%d",&n,&m);
s[0]=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&p);
s[i]=p;
}
for(int i=1;i<=m;i++)
scanf("%d",&t[i]);
solve(1);
if(cmplt)
{
printf("\n");
solve(2);
}
return 0;
}
评测结果
三、JPEG解码
1.题目内容
2.题解分析
个人觉得是个小模拟。为什么这样说呢,因为题目把操作的步骤一步步说清楚了,计算的公式也给齐了,数据量也不大,8x8矩阵,所以O( n 2 n^2 n2)计算完全没问题,按部就班就行了。
主要难点有:
1.z形读入数据;
2.离散余弦逆变换计算。
首先来讲讲z形读入。
观察每条斜着的路径,会发现坐标会按如下规律变化:
x--,y++;//右上角方向
x++,y--;//左下角方向
然后运动到边缘,有个平移,然后转向操作,所以我们声明个方向标记down,当在边缘时,平移一下并改变方向标记。根据方向来决定如何移动;
但这样只能完成一个三角形的z形读入,即矩形一半(用左下角与右上角连接的对角线分割)。再观察,发现在上下半部分切换时平移方法会改变,所以增加一个上部分标记upart,根据upart来决定到边缘时如何平移,再用左下角角的坐标判定上下部分切换,改变upart值即可。
if(upart&&!down&&x==0)
y++,down=1;
else if(upart&&down&&y==0)
x++,down=0;
else if(!upart&&!down&&y==7)
x++,down=1;
else if(!upart&&down&&x==7)
y++,down=0;
else if(down)
x++,y--;
else
x--,y++;
if(x==7&&y==0)
upart=0;
然后是离散余弦逆变换。根据这个公式计算:
这个公式的意思即对于每个
M
i
,
j
′
M'_{i,j}
Mi,j′,把i,和j代入,然后对u和v从0到7的值按公式求和。
理解之后直接代入即可。
double cal(int i,int j)
{
double res=0.0,pi=acos(-1),a=sqrt(1/2.0);
for(int u=0;u<8;u++)
for(int v=0;v<8;v++)
res+=(u==0?a:1.0)*(v==0?a:1.0)*m[u][v]*cos(pi/8.0*(i+0.5)*(u*1.0))*cos(pi/8.0*(j+0.5)*(v*1.0));
return 0.25*res;
}
最后再说下四舍五入,我的做法是获取其小数部分,判断它是否大于0.5,是就输出整数部分+1,否就直接输出整数部分
double c=num-(int)num;
if(c<0.5)
printf("%d ",(int)num);
else
printf("%d ",(int)num+1);
3.完整代码
#include<bits/stdc++.h>
using namespace std;
int n,T;
double q[10][10];
double m[10][10];
void print()
{
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
printf("%d ",(int)m[i][j]);
printf("\n");
}
}
double cal(int i,int j)
{
double res=0.0,pi=acos(-1),a=sqrt(1/2.0);
for(int u=0;u<8;u++)
for(int v=0;v<8;v++)
res+=(u==0?a:1.0)*(v==0?a:1.0)*m[u][v]*cos(pi/8.0*(i+0.5)*(u*1.0))*cos(pi/8.0*(j+0.5)*(v*1.0));
return 0.25*res;
}
int main()
{
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
scanf("%lf",&q[i][j]);
scanf("%d",&n);
scanf("%d",&T);
//z形写入矩阵m
int x=0,y=0;
bool down=0,upart=1;
for(int k=0;k<n;k++)
{
double s;
scanf("%lf",&s);
m[x][y]=s;
if(upart&&!down&&x==0)
y++,down=1;
else if(upart&&down&&y==0)
x++,down=0;
else if(!upart&&!down&&y==7)
x++,down=1;
else if(!upart&&down&&x==7)
y++,down=0;
else if(down)
x++,y--;
else
x--,y++;
if(x==7&&y==0)
upart=0;
}
if(T==0)
print();
else
{
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
m[i][j]*=q[i][j];
if(T==1)
print();
else
{
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
double num=cal(i,j)+128;
if(num>255)
printf("255 ");
else if(num<0)
printf("0 ");
else
{
double c=num-(int)num;
if(c<0.5)
printf("%d ",(int)num);
else
printf("%d ",(int)num+1);
}
}
printf("\n");
}
}
}
return 0;
}
评测结果
都看到这里了,能否给个小小的赞呢?