ccf-csp 2014春季真题题解


  1. 相反数
    问题描述
      有 N 个非零且各不相同的整数。请你编一个程序求出它们中有多少对相反数(a 和 -a 为一对相反数)。
    输入格式
      第一行包含一个正整数 N。(1 ≤ N ≤ 500)。
      第二行为 N 个用单个空格隔开的非零整数,每个数的绝对值不超过1000,保证这些整数各不相同。
    输出格式
      只输出一个整数,即这 N 个数中包含多少对相反数。
    样例输入
    5
    1 2 3 -1 -2
    样例输出
    2
    题解:
      使用数组记录正负数字出现的次数,使用offset就可以只开一个数组。

代码:

#include <iostream>

using namespace std;

const int MAXN = 1010;
int arr[2 * MAXN];

int main(){
    int n, ans = 0, a;

    cin >> n;
    while(n --){
        cin >> a;
        arr[a + MAXN] ++;
        ans += arr[-a + MAXN];
    }
    cout << ans << endl;
    return 0;
}

  1. 窗口
    问题描述
      在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域。窗口的边界上的点也属于该窗口。窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容。
      当你点击屏幕上一个点的时候,你就选择了处于被点击位置的最顶层窗口,并且这个窗口就会被移到所有窗口的最顶层,而剩余的窗口的层次顺序不变。如果你点击的位置不属于任何窗口,则系统会忽略你这次点击。
      现在我们希望你写一个程序模拟点击窗口的过程。
    输入格式
      输入的第一行有两个正整数,即 N 和 M。(1 ≤ N ≤ 10,1 ≤ M ≤ 10)
      接下来 N 行按照从最下层到最顶层的顺序给出 N 个窗口的位置。 每行包含四个非负整数 x1, y1, x2, y2,表示该窗口的一对顶点坐标分别为 (x1, y1) 和 (x2, y2)。保证 x1 < x2,y1 2。
      接下来 M 行每行包含两个非负整数 x, y,表示一次鼠标点击的坐标。
      题目中涉及到的所有点和矩形的顶点的 x, y 坐标分别不超过 2559 和  1439。
    输出格式
      输出包括 M 行,每一行表示一次鼠标点击的结果。如果该次鼠标点击选择了一个窗口,则输出这个窗口的编号(窗口按照输入中的顺序从 1 编号到 N);如果没有,则输出"IGNORED"(不含双引号)。
    样例输入
    3 4
    0 0 4 4
    1 1 5 5
    2 2 6 6
    1 1
    0 0
    4 4
    0 5
    样例输出
    2
    1
    1
    IGNORED
    样例说明
      第一次点击的位置同时属于第 1 和第 2 个窗口,但是由于第 2 个窗口在上面,它被选择并且被置于顶层。
      第二次点击的位置只属于第 1 个窗口,因此该次点击选择了此窗口并将其置于顶层。现在的三个窗口的层次关系与初始状态恰好相反了。
      第三次点击的位置同时属于三个窗口的范围,但是由于现在第 1 个窗口处于顶层,它被选择。
      最后点击的 (0, 5) 不属于任何窗口。
    题解:
      开一个数组记录窗体,下标越大的窗体更靠近顶层,依次模拟点击,从后往前遍历数组第一个满足要求的窗体就是点击到的窗体,然后重排数组使得刚刚被点击的窗体位于最顶层。

代码:

#include <iostream>

using namespace std;

struct node{
    int x1, x2, y1, y2;
    int num;
};
const int MAXN = 20;
node arr[MAXN];

int main(){
    int n, m, a, b;

    cin >> n >> m;
    for(int i = 1; i <= n; i ++){
        cin >> arr[i].x1 >> arr[i].y1 >> arr[i].x2 >> arr[i].y2;
        arr[i].num = i;
    }
    while(m --){
        cin >> a >> b;
        int t = -1;
        //从后往前遍历第一个满足要求的窗体就是点击到的窗体
        for(int i = n; i >= 1; i --){
            if(a >= arr[i].x1 && a <= arr[i].x2 && b >= arr[i].y1 && b <= arr[i].y2){
                t = i;
                break;
            }
        }
        // -1是没点击到任何窗体
        if(t == -1)
            cout << "IGNORED" << endl;
        else{
            cout << arr[t].num << endl;
            node tmp = arr[t];
            //刚刚点击的窗体置于最顶层
            for(int i = t; i < n; i ++)
                arr[i] = arr[i + 1];
            arr[n] = tmp;
        }
    }
    return 0;
}

  1. 命令行选项
    问题描述
      请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项。每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔。这些字符串中的第一个为该命令行工具的名字,由小写字母组成,你的程序不用对它进行处理。在工具名字之后可能会包含若干选项,然后可能会包含一 些不是选项的参数。
      选项有两类:带参数的选项和不带参数的选项。一个合法的无参数选项的形式是一个减号后面跟单个小写字母,如"-a" 或"-b"。而带参数选项则由两个由空格分隔的字符串构成,前者的格式要求与无参数选项相同,后者则是该选项的参数,是由小写字母,数字和减号组成的非空字符串。
      该命令行工具的作者提供给你一个格式字符串以指定他的命令行工具需要接受哪些选项。这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。例如, “ab: m:” 表示该程序接受三种选项,即"-a"(不带参数),"-b"(带参数), 以及"-m"(带参数)。
      命令行工具的作者准备了若干条命令行用以测试你的程序。对于每个命令行,你的工具应当一直向后分析。当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。
    输入格式
      输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。
      输入的第二行是一个正整数 N(1 ≤ N ≤ 20),表示你需要处理的命令行的个数。
      接下来有 N 行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。
    输出格式
      输出有 N 行。其中第 i 行以"Case i:" 开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。如果一个选项在命令行中出现了多次,只输出一次。如果一个带参数的选项在命令行中出 现了多次,只输出最后一次出现时所带的参数。
    样例输入
    albw:x
    4
    ls -a -l -a documents -b
    ls
    ls -w 10 -x -w 15
    ls -a -b -c -d -e -l
    样例输出
    Case 1: -a -l
    Case 2:
    Case 3: -w 15 -x
    Case 4: -a -b
    题解:
      开一个26 * 2的数组,分别代表26个字符是否是带参数、不带参数选项,为true则代表当前命令有这个选项,处理好了第一行后,使用stringstream将读入的字符串存储在vector< string > v内,然后依次处理判断是不是带参数、不带参数,使用vector< string > ans来记录答案,大小为26,代表26个字母(不需要把有参数无参数分开来,因为一个字母只会对应有参无参一种,否则有冲突),如果是不带参数的,则在对应位置填上任意字符串,如果带参数,则需要存储带的参数,最后依据vector< string > ans 内对应位置是否为空来输出答案,详见代码。

代码:

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

bool op[30][2];

int main(){
    int n;
    string s;

    cin >> s;
    for(int i = 0; i < s.length(); i ++){
    	//带参数
        if(i + 1 < s.length() && s[i + 1] == ':'){
            op[s[i] - 'a'][1] = true;
            ++ i;
        }else{	//不带参数
            op[s[i] - 'a'][0] = true;
        }
    }

    cin >> n;
    getchar();
    for(int i = 1; i <= n; i ++){
        vector<string> v, ans(30);
        //将一行数据存储到string数组中
        getline(cin, s);
        stringstream ss(s);
        while(ss >> s){
            v.push_back(s);
        }
		//第一个字符串是ls 跳过
        for(int i = 1; i < v.size(); i ++){
        	//不合法的输入
            if(v[i][0] != '-' || v[i].size() > 2)
                break;
            //不带参数
            if(op[v[i][1] - 'a'][0])
                ans[v[i][1] - 'a'] = "-";
            //带参数
            else if(op[v[i][1] - 'a'][1] && i + 1 < v.size()){
                ans[v[i][1] - 'a'] = v[i + 1];
                ++ i;
            }else
                break;
        }
        cout << "Case " << i << ": ";
        for(int i = 0; i < 26; i ++){
        	//为空代表没有这个选项
            if(ans[i].empty())
                continue;
            cout << "-" << (char)(i + 'a') << " ";
            //带参数还需要输出参数
            if(op[i][1])
                cout << ans[i] << " ";
        }
        cout << endl;
    }

    return 0;
}

  1. 无线网络
    问题描述
      目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
      除此以外,另有 m 个可以摆放无线路由器的位置。你可以在这些位置中选择至多 k 个增设新的路由器。
      你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。请问在最优方案下中转路由器的最少个数是多少?
    输入格式
      第一行包含四个正整数 n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
      接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线 路由器在 (xi, yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
      接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi, yi) 点处可以增设 一个路由器。
      输入中所有的坐标的绝对值不超过 108,保证输入中的坐标各不相同。
    输出格式
      输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路 由器到第 2 个路由器最少经过的中转路由器的个数。
    样例输入
    5 3 1 3
    0 0
    5 5
    0 3
    0 5
    3 5
    3 3
    4 4
    3 0
    样例输出
    2
    题解:
      一道图论题,dist[a][b]代表 使用b个临时节点 从 1 到 a 的最小值,一开始将{1, 0},入队,最后bfs收敛后,遍历dist[2][i], i为0 到 k ,取最小值即可。

代码:

#include <iostream>
#include <queue>
#include <climits>
#include <cstring>

using namespace std;

const int MAXN = 210;
typedef pair<int, int> PII;
PII arr[MAXN];
int dist[MAXN][MAXN], n, m, k, r, cnt = 0;
int head[MAXN], ver[MAXN * MAXN], nxt[MAXN * MAXN]; 
//小于r才有边
bool check(int a, int b, int c, int d, int r){
    return (long long)(a - b) * (a - b) + (long long)(c - d) * (c - d) <= (long long) r * r;
}
//a -》 b 建立有向边
void add(int a, int b){
    ver[++ cnt] = b;
    nxt[cnt] = head[a];
    head[a] = cnt;
}

void bfs(){
    memset(dist, 0x3f3f3f3f, sizeof(dist));
    dist[1][0] = 0;
    queue<PII> Q;

    Q.push({1, 0});
    while(!Q.empty()){
        PII cur = Q.front();
        Q.pop();
        //遍历所有邻接点
        for(int i = head[cur.first]; i; i = nxt[i]){
            int u = ver[i], v = cur.second;
            //如果是临时点,则 +1
            if(ver[i] > n)
                v ++;
            if(dist[u][v] > dist[cur.first][cur.second] + 1){
                dist[u][v] = dist[cur.first][cur.second] + 1;
               	//只允许使用k个临时点
                if(v <= k)
                    Q.push({u, v});
            }
        }
    }
}

int main(){
    int a, b;

    cin >> n >> m >> k >> r;
    for(int i = 1; i <= n + m; i ++){
        cin >> arr[i].first >> arr[i].second;
        for(int j = 1; j < i; j ++){
        	//建图
            if(check(arr[i].first, arr[j].first, arr[i].second, arr[j].second, r)){
                add(i, j);
                add(j, i);
            }
        }
    }

    bfs();
    int ans = INT_MAX;
    for(int i = 0; i <= k; i ++){
        ans = min(ans, dist[2][i]);
    }
    cout << ans - 1 << endl;

    return 0;
}

  1. 任务调度
    问题描述
      有若干个任务需要在一台机器上运行。它们之间没有依赖关系,因此 可以被按照任意顺序执行。
      该机器有两个 CPU 和一个 GPU。对于每个任务,你可以为它分配不 同的硬件资源:
      1. 在单个 CPU 上运行。
      2. 在两个 CPU 上同时运行。
      3. 在单个 CPU 和 GPU 上同时运行。
      4. 在两个 CPU 和 GPU 上同时运行。
      一个任务开始执行以后,将会独占它所用到的所有硬件资源,不得中 断,直到执行结束为止。第 i 个任务用单个 CPU,两个 CPU,单个 CPU 加 GPU,两个 CPU 加 GPU 运行所消耗的时间分别为 ai,bi,ci 和 di。
      现在需要你计算出至少需要花多少时间可以把所有给定的任务完成。
    输入格式
      输入的第一行只有一个正整数 n(1 ≤ n ≤ 40), 是总共需要执行的任 务个数。
      接下来的 n 行每行有四个正整数 ai, bi, ci, di(ai, bi, ci, di 均不超过 10), 以空格隔开。
    输出格式
      输出只有一个整数,即完成给定的所有任务所需的最少时间。
    样例输入
    3
    4 4 2 2
    7 4 7 4
    3 3 3 3
    样例输出
    7
    样例说明
      有很多种调度方案可以在 7 个时间单位里完成给定的三个任务,以下是其中的一种方案:
      同时运行第一个任务(单 CPU 加上 GPU)和第三个任务(单 CPU), 它们分别在时刻 2 和时刻 3 完成。在时刻 3 开始双 CPU 运行任务 2,在 时刻 7 完成。
    题解:
      暂无题解。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值