您的位置:首页 > 大数据 > 人工智能

【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

2016-08-30 20:50 323 查看
【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

这次的题总体上相对前三次偏简单。由于实力有限,就分析前四题。

   
试题编号:    201503-1
试题名称:    图像旋转
时间限制:    5.0s
内存限制:    256.0MB
问题描述:   
问题描述
  旋转是图像处理的基本操作,在这个问题中,你需要将一个图像逆时针旋转90度。
  计算机中的图像表示可以用一个矩阵来表示,为了旋转一个图像,只需要将对应的矩阵旋转即可。
输入格式
  输入的第一行包含两个整数n, m,分别表示图像矩阵的行数和列数。
  接下来n行每行包含m个整数,表示输入的图像。
输出格式
  输出m行,每行包含n个整数,表示原始矩阵逆时针旋转90度后的矩阵。
样例输入
2 3
1 5 3
3 2 4
样例输出
3 4
5 2
1 3
评测用例规模与约定
  1 ≤ n, m ≤ 1,000,矩阵中的数都是不超过1000的非负整数。

实(dou)力(bi)分析:逆时针旋转90°就是将矩阵的第i行变为第i列,第j行变为第m+1-j列,分析出来就没什么问题了。其次很重要的就是输出格式,每行的最后一个数字之后是没有空格的。签到题100分到手。
本人源码:

#include<iostream>
using namespace std;
int in[1010][1010], out[1010][1010];
int main(){
    int i, j, m, n;
    cin>>n>>m;
    for(i=1;i<=n;++i) for(j=1;j<=m;++j) cin>>in[i][j];
    for(i=1;i<=n;++i) for(j=1;j<=m;++j) out[m+1-j][i]=in[i][j];
    for(i=1;i<=m;++i) {
        for(j=1;j<=n;++j){
            if(j==1) cout<<out[i][j];
            else cout<<' '<<out[i][j];
        }
        cout<<endl;
    }
    return 0;
}

试题编号:    201503-2

试题名称:    数字排序

时间限制:    1.0s

内存限制:    256.0MB

问题描述:   

问题描述

  给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出。

输入格式

  输入的第一行包含一个整数n,表示给定数字的个数。

  第二行包含n个整数,相邻的整数之间用一个空格分隔,表示所给定的整数。

输出格式

  输出多行,每行包含两个整数,分别表示一个给定的整数和它出现的次数。按出现次数递减的顺序输出。如果两个整数出现的次数一样多,则先输出值较小的,然后输出值较大的。

样例输入

12

5 2 3 3 1 3 4 2 5 2 3 5

样例输出

3 4

2 3

5 3

1 1

4 1

评测用例规模与约定

  1 ≤ n ≤ 1000,给出的数都是不超过1000的非负整数。

实(dou)力(bi)分析:这题主要是结构体的二级排序吧(反正我是这样做的),用好algorithm里的sort(),把过程模拟下就行了。由于给出的数是非负整数,所以0也要考虑进去,并且输入的数不一定小于n,所以在排序时应该sort(a, a+1005, cmp)。[b]

本人源码:
[/b]

#include<iostream>
#include<algorithm>
using namespace std;
struct num{
    int val;
    int t;
};
bool cmp(num a, num b){
    if(a.t != b.t) return a.t>b.t;
    return a.val<b.val;
}
num a[1005];
int main(){
    int n, i, x;
    for(i=0;i<=1000;++i){
        a[i].val = i;
    }
    cin>>n;
    for(i=1;i<=n;++i){
        cin>>x;
        ++a[x].t;
    }
    sort(a, a+1005, cmp);
    for(i=0;i<=n;++i){
        if(a[i].t==0) break;
        cout<<a[i].val<<' '<<a[i].t<<endl;
    }
    return 0;
}

 试题编号:    201503-3
试题名称:    节日
时间限制:    1.0s
内存限制:    256.0MB
问题描述:   
问题描述
  有一类节日的日期并不是固定的,而是以“a月的第b个星期c”的形式定下来的,比如说母亲节就定为每年的五月的第二个星期日。
  现在,给你a,b,c和y1, y2(1850 ≤ y1, y2 ≤ 2050),希望你输出从公元y1年到公元y2年间的每年的a月的第b个星期c的日期。
  提示:关于闰年的规则:年份是400的整数倍时是闰年,否则年份是4的倍数并且不是100的倍数时是闰年,其他年份都不是闰年。例如1900年就不是闰年,而2000年是闰年。
  为了方便你推算,已知1850年1月1日是星期二。
输入格式
  输入包含恰好一行,有五个整数a, b, c, y1, y2。其中c=1, 2, ……, 6, 7分别表示星期一、二、……、六、日。
输出格式
  对于y1和y2之间的每一个年份,包括y1和y2,按照年份从小到大的顺序输出一行。
  如果该年的a月第b个星期c确实存在,则以"yyyy/mm/dd"的格式输出,即输出四位数的年份,两位数的月份,两位数的日期,中间用斜杠“/”分隔,位数不足时前补零。
  如果该年的a月第b个星期c并不存在,则输出"none"(不包含双引号)。
样例输入
5 2 7 2014 2015
样例输出
2014/05/11
2015/05/10
评测用例规模与约定
  所有评测用例都满足:1 ≤ a ≤ 12,1 ≤ b ≤ 5,1 ≤ c ≤ 7,1850 ≤ y1, y2 ≤ 2050。

实(dou)力(bi)分析:有点麻烦的模拟题。更像小学的奥数题,总之把这题逻辑思路搞清楚了,一般过了样例就可以拿满分了。(l = min(y1), r = max(y2)也许纯属我的强迫症吧。。毕竟已经说了y1到y2年之间)[b]

本人源码:
[/b]

#include<iostream>
using namespace std;
int day[2][13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
                , 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool isRunNian(int y){
    if(y%400==0) return true;
    else if((y%4==0) && (y%100)) return true;
    else return false;
}
int main(){
    int y1, y2, a, b, c;
    cin>>a>>b>>c>>y1>>y2;
    int i, l = min(y1, y2), r = max(y1, y2);
    for(i=l;i<=r;++i){
        int curY = 1850;
        int curM = 1;
        int days = 0;
        int xq = 1;
        int d;
        while(curY < i){
            if(isRunNian(curY)) days+=366;
            else days+=365;
            ++curY;
        }
        while(curM < a){
            days += day[isRunNian(curY)][curM];
            ++curM;
        }
        xq = 1 + days%7;
        if(xq > c) d =(c+7-xq)+(b-1)*7;
        else d = (c-xq)+(b-1)*7;
        if(d>day[isRunNian(curY)][curM]) cout<<"none"<<endl;
        else {
            cout<<curY<<'/';
            if(a/10) cout<<a;
            else cout<<'0'<<a;
            cout<<'/';
            if(d/10) cout<<d;
            else cout<<'0'<<d;
            cout<<endl;
        }
    }
    return 0;


试题编号:    201503-4
试题名称:    网络延时
时间限制:    1.0s
内存限制:    256.0MB
问题描述:   
问题描述
  给定一个公司的网络,由n台交换机和m台终端电脑组成,交换机与交换机、交换机与电脑之间使用网络连接。交换机按层级设置,编号为1的交换机为根交换机,层级为1。其他的交换机都连接到一台比自己上一层的交换机上,其层级为对应交换机的层级加1。所有的终端电脑都直接连接到交换机上。
  当信息在电脑、交换机之间传递时,每一步只能通过自己传递到自己所连接的另一台电脑或交换机。请问,电脑与电脑之间传递消息、或者电脑与交换机之间传递消息、或者交换机与交换机之间传递消息最多需要多少步。
输入格式
  输入的第一行包含两个整数n, m,分别表示交换机的台数和终端电脑的台数。
  第二行包含n - 1个整数,分别表示第2、3、……、n台交换机所连接的比自己上一层的交换机的编号。第i台交换机所连接的上一层的交换机编号一定比自己的编号小。
  第三行包含m个整数,分别表示第1、2、……、m台终端电脑所连接的交换机的编号。
输出格式
  输出一个整数,表示消息传递最多需要的步数。
样例输入
4 2
1 1 3
2 1
样例输出
4
样例说明
  样例的网络连接模式如下,其中圆圈表示交换机,方框表示电脑:



  其中电脑1与交换机4之间的消息传递花费的时间最长,为4个单位时间。
样例输入
4 4
1 2 2
3 4 4 4
样例输出
4
样例说明
  样例的网络连接模式如下:



  其中电脑1与电脑4之间的消息传递花费的时间最长,为4个单位时间。
评测用例规模与约定
  前30%的评测用例满足:n ≤ 5, m ≤ 5。
  前50%的评测用例满足:n ≤ 20, m ≤ 20。
  前70%的评测用例满足:n ≤ 100, m ≤ 100。
  所有评测用例都满足:1 ≤ n ≤ 10000,1 ≤ m ≤ 10000。

实(dou)力(bi)分析:图有 n+m个顶点,n+m-1条边,顶点之间两两连通,很容易看出来这是一棵树。问题抽象后就是求树中相距最远的两个叶子结点间的距离,这个距离又叫做树的直径。那天刚好带了求树的直径的模版过去,所以没动多少脑子就写完了。不过这题只拿了70分,模版的时间复杂度O(|V|+|E|)应该没什么问题,然后就想起了考试那天使用了cin来输入,在最后30%比较大输入量的评测用例中超时了,欲哭无泪,还是对大输入量不够敏感。模版的思路就是先dfs找出距离根结点0最远的叶子结点u,然后再对u一次dfs找出距离u最远的叶子结点。[b]

本人源码:
[/b]

#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 222222;
int edgeCount, firstEdge
, to
, length
, nextEdge
;
vector<int> dist;
void addEdge(int u, int v, int w){
    to[edgeCount] = v;
    length[edgeCount] = w;
    nextEdge[edgeCount] = firstEdge[u];
    firstEdge[u] = edgeCount++;
}
void dfs(int p, int u, int d){
    dist[u] = d;
    for(int iter = firstEdge[u]; iter != -1; iter = nextEdge[iter]){
        if(to[iter] != p){
            dfs(u, to[iter], d+length[iter]);
        }
    }
}
int getDiameter(int nodeCount, vector <pair <pair <int, int>, int> > edges){
    edgeCount = 0;
    memset(firstEdge, -1, sizeof(firstEdge));
    for(vector<pair <pair <int, int>, int> >::iterator iter = edges.begin(); iter != edges.end();++iter){
        addEdge(iter->first.first,iter->first.second, iter->second);
        addEdge(iter->first.second,iter->first.first, iter->second);
    }
    dist.resize(nodeCount);
    dfs(-1, 0, 0);
    int u = max_element(dist.begin(), dist.end()) - dist.begin();
    dfs(-1,u,0);
    return *max_element(dist.begin(), dist.end());
}
int main(){
    int n, m, v;
    vector <pair <pair <int, int>, int> > edges;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;++i){
        scanf("%d",&v);
        edges.push_back(make_pair(make_pair(v-1, i),1));
        edges.push_back(make_pair(make_pair(i, v-1),1));
    }
    for(int i=n;i<=n+m-1;++i){
        scanf("%d",&v);
        edges.push_back(make_pair(make_pair(v-1, i),1));
        edges.push_back(make_pair(make_pair(i, v-1),1));
    }
    printf("%d\n",getDiameter(n+m, edges));
    return 0;
}

试题编号:    201503-5
试题名称:    最小花费

时间限制:    4.0s

内存限制:    256.0MB

问题描述:   

问题描述

  C国共有n个城市。有n-1条双向道路,每条道路连接两个城市,任意两个城市之间能互相到达。小R来到C国旅行,他共规划了m条旅行的路线,第i条旅行路线的起点是si,终点是ti。在旅行过程中,小R每行走一单位长度的路需要吃一单位的食物。C国的食物只能在各个城市中买到,而且不同城市的食物价格可能不同。

  然而,小R不希望在旅行中为了购买较低价的粮食而绕远路,因此他总会选择最近的路走。现在,请你计算小R规划的每条旅行路线的最小花费是多少。

输入格式

  第一行包含2个整数n和m。

  第二行包含n个整数。第i个整数wi表示城市i的食物价格。

  接下来n-1行,每行包括3个整数u, v, e,表示城市u和城市v之间有一条长为e的双向道路。

  接下来m行,每行包含2个整数si和ti,分别表示一条旅行路线的起点和终点。

输出格式

  输出m行,分别代表每一条旅行方案的最小花费。

样例输入

6 4

1 7 3 2 5 6

1 2 4

1 3 5

2 4 1

3 5 2

3 6 1

2 5

4 6

6 4

5 6

样例输出

35

16

26

13

样例说明

  对于第一条路线,小R会经过2->1->3->5。其中在城市2处以7的价格购买4单位粮食,到城市1时全部吃完,并用1的价格购买7单位粮食,然后到达终点。

评测用例规模与约定

  前10%的评测用例满足:n, m ≤ 20, wi ≤ 20;

  前30%的评测用例满足:n, m ≤ 200;

  另有40%的评测用例满足:一个城市至多与其它两个城市相连。

  所有评测用例都满足:1 ≤ n, m ≤ 105,1 ≤ wi ≤ 106,1 ≤ e ≤ 10000。
实(dou)力(bi)分(mai)析(meng):看到时间限制是4s的那一刻对于做这道题我是拒绝的。不过前30%数据范围比较小,说说思路吧。先从si开始dfs找到通向ti的所有通路,然后dp找出最小花费。

事实证明写题解是件体力活,写完千万不要忘记保存,否则会重写一遍。。。

最后,吹一年的370镇楼。



Our Conquest is the Celestial Sea.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: