第一次CCF计算机软件能力认证

AcWing 3197. 相反数

有 N 个非零且各不相同的整数。

请你编一个程序求出它们中有多少对相反数(a 和 −a 为一对相反数)。

输入格式

第一行包含一个正整数 N。

第二行为 N 个用单个空格隔开的非零整数,每个数的绝对值不超过 1000,保证这些整数各不相同。

输出格式

只输出一个整数,即这 N 个数中包含多少对相反数。

数据范围

1≤N≤500

输入样例

5
1 2 3 -1 -2

输出样例

2

flag数组标记计数即可 暴力AC: 

#include<iostream>
using namespace std;
const int N=1e3+5;
int n,flag[N],ans;
int main(){
    cin>>n;
    while(n--){
        int x;
        cin>>x;
        flag[abs(x)]++;
    }
    for(int i=1;i<=1000;i++) if(flag[i]==2) ans++;
    cout<<ans<<endl;
    return 0;
}

AcWing 3198. 窗口

在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域。

窗口的边界上的点也属于该窗口。

窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容。

当你点击屏幕上一个点的时候,你就选择了处于被点击位置的最顶层窗口,并且这个窗口就会被移到所有窗口的最顶层,而剩余的窗口的层次顺序不变。

如果你点击的位置不属于任何窗口,则系统会忽略你这次点击。

现在我们希望你写一个程序模拟点击窗口的过程。

输入格式

输入的第一行有两个正整数,即 N 和 M。

接下来 N 行按照从最下层到最顶层的顺序给出 N 个窗口的位置。

每行包含四个非负整数 x1,y1,x2,y2,表示该窗口的一对顶点坐标分别为 (x1,y1) 和 (x2,y2)。保证 x1<x2,y1<y2。

接下来 M 行每行包含两个非负整数 x,y,表示一次鼠标点击的坐标。

题目中涉及到的所有点和矩形的顶点的 x,y 坐标分别不超过 2559 和 1439。

输出格式

输出包括 M 行,每一行表示一次鼠标点击的结果。

如果该次鼠标点击选择了一个窗口,则输出这个窗口的编号(窗口按照输入中的顺序从 1 编号到 N)。

如果没有,则输出 IGNORED

数据范围

1≤N,M≤10

输入样例

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) 不属于任何窗口。

模拟 + map AC:

#include<iostream>
#include<cstring>
#include<map>
using namespace std;
typedef pair<int,int>pii;
typedef pair<pii,pii>piiii;
int n,m,flag[2599][1499];
map<int,piiii>mapp;
int main(){
    cin>>n>>m;
    memset(flag,-1,sizeof flag);
    for(int i=1;i<=n;i++){
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        mapp[i]=make_pair(make_pair(x1,y1),make_pair(x2,y2));
        for(int j=x1;j<=x2;j++) for(int k=y1;k<=y2;k++) flag[j][k]=i;
    }
    while(m--){
        int x,y,ans;
        cin>>x>>y;
        ans=flag[x][y];
        if(ans>0){
            cout<<ans<<endl;
            piiii t=mapp[ans];
            for(int i=t.first.first;i<=t.second.first;i++)
                for(int j=t.first.second;j<=t.second.second;j++)
                    flag[i][j]=ans;
        }
        else cout<<"IGNORED"<<endl;
    }
    return 0;
}

AcWing 3199. 命令行选项

请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项。

每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔。

这些字符串中的第一个为该命令行工具的名字,由小写字母组成,你的程序不用对它进行处理。

在工具名字之后可能会包含若干选项,然后可能会包含一些不是选项的参数。

选项有两类:带参数的选项和不带参数的选项。

一个合法的无参数选项的形式是一个减号后面跟单个小写字母,如 -a 或 -b

而带参数选项则由两个由空格分隔的字符串构成,前者的格式要求与无参数选项相同,后者则是该选项的参数,是由小写字母,数字和减号组成的非空字符串。

该命令行工具的作者提供给你一个格式字符串以指定他的命令行工具需要接受哪些选项。

这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。

如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。

例如,ab:m: 表示该程序接受三种选项,即 -a(不带参数),-b(带参数),以及 -m(带参数)。

命令行工具的作者准备了若干条命令行用以测试你的程序。

对于每个命令行,你的工具应当一直向后分析。

当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。

命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。

输入格式

输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。

格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。

输入的第二行是一个正整数 N,表示你需要处理的命令行的个数。

接下来有 N 行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。

输出格式

输出有 N 行。其中第 i 行以 Case i: 开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。

如果一个选项在命令行中出现了多次,只输出一次。

如果一个带参数的选项在命令行中出现了多次,只输出最后一次出现时所带的参数。

数据范围

1≤N≤20,
对于每组数据,所有命令行工具的名字一定相同,且由小写字母构成。

输入样例

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

模拟AC: 

#include<iostream>
#include<map>
#include<vector>
#include<cstring>
using namespace std;
int n,flag_CS[26];// flag_CS用于标记字符是否为命令
int main(){
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++){
        flag_CS[s[i]-'a']=-1;// 将字符标记为无参数命令
        if(s[i]==':') flag_CS[s[i-1]-'a']=1;// 如果字符后有':',标记为有参数命令
    }
    cin>>n;
    getchar();// 吸收换行符,避免影响后续的getline读取
    for(int c=1;c<=n;c++){
        string line;
        getline(cin,line);// 读取一行命令
        vector<string>ans;// 定义字符串向量ans,用于存储分割后的命令
        string t;
        // 分割命令行字符串
        for(int i=0;i<line.size();i++){
            if(line[i]!=' ') t+=line[i];
            if(line[i]==' ') {ans.push_back(t); t="";}
            if(i+1==line.size()) ans.push_back(t);
        }
        map<int,string>mapp;// 定义映射容器mapp,用于存储命令和对应的值
        for(int i=1;i<ans.size();i++){
            // 检查命令格式是否正确
            if(ans[i][0]!='-'||ans[i][1]<'a'||ans[i].size()!=2) break; 
            int x=ans[i][1]-'a';
            // 如果无参数命令
            if(flag_CS[x]==-1) mapp[x]="无参数";
            // 如果有参数命令
            else if(flag_CS[x]==1&&(i+1)<ans.size()) mapp[x]=ans[i+1],i++;
            // 如果命令格式错误,跳出循环
            else break;
        }
        //格式化输出
        cout<<"Case "<<c<<":";
        for(auto x:mapp){
            if(flag_CS[x.first]==-1) cout<<" -"<<(char)('a'+x.first);
            else cout<<" -"<<(char)('a'+x.first)<<" "<<x.second;
        }
        cout<<endl;
    }
    return 0;
}

AcWing 3200. 无线网络

目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。

任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。

除此以外,另有 m 个可以摆放无线路由器的位置。

你可以在这些位置中选择至多 k 个增设新的路由器。

你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。

请问在最优方案下中转路由器的最少个数是多少?

输入格式

第一行包含四个正整数 n,m,k,r。

接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线路由器在 (xi,yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。

接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi,yi) 点处可以增设一个路由器。

输入中所有的坐标的绝对值不超过 10^8,保证输入中的坐标各不相同。

输出格式

输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路由器到第 2 个路由器最少经过的中转路由器的个数。

数据范围

2≤n≤100
1≤k≤m≤100
1≤r≤10^8

输入样例

5 3 1 3
0 0
5 5
0 3
0 5
3 5
3 3
4 4
3 0

输出样例

2

bfs+贪心 AC:

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
int n,m,k,r,dist[205][205];//第i个点的路径中增设路由器的数量为j的最短距离
vector<int>G[205];
map<int,pair<int,int>>mapip;
int bfs(){
    memset(dist,0x3f,sizeof dist);
    queue<pair<int,int>>q;
    q.push({1,0});//起点编号为1,初始路由器数量为0
    dist[1][0]=0;
    while(!q.empty()){
        auto t=q.front();
        q.pop();
        int id=t.first,cnt=t.second;
        for(int nextp:G[id]){
            int nextCnt=cnt;
            if(nextp>n) nextCnt++;//如果移动到新增路由器,则计数增加
            if(nextCnt<=k){//如果增设的路由器数量不超过k,则继续搜索
                if(dist[nextp][nextCnt]>dist[id][cnt]+1){//如果找到更短的路径,则更新dist数组,并将新的节点加入队列
                    dist[nextp][nextCnt]=dist[id][cnt]+1;
                    q.push({nextp,nextCnt});
                }
            }
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=0;i<=k;i++) ans=min(ans,dist[2][i]);//终点编号为2  
    return ans-1;
}
int main(){
    cin>>n>>m>>k>>r;
    for(int i=1;i<=n+m;i++){
        int x,y;
        cin>>x>>y;
        mapip[i]=make_pair(x,y);
    }
    for(int i=1;i<=n+m;i++){
        for(int j=i+1;j<=n+m;j++){
            int ax=mapip[i].first,ay=mapip[i].second,bx=mapip[j].first,by=mapip[j].second;
            if((ll)(ax-bx)*(ax-bx)+(ll)(ay-by)*(ay-by)<=(ll)r*r){
                G[i].push_back(j);G[j].push_back(i);
            }
        }
    }
    cout<<bfs()<<endl;
    return 0;
}

AcWing 3201. 任务调度

有若干个任务需要在一台机器上运行。

它们之间没有依赖关系,因此可以被按照任意顺序执行。

该机器有两个 CPU 和一个 GPU。

对于每个任务,你可以为它分配不同的硬件资源:

  1. 在单个 CPU 上运行。
  2. 在两个 CPU 上同时运行。
  3. 在单个 CPU 和 GPU 上同时运行。
  4. 在两个 CPU 和 GPU 上同时运行。

一个任务开始执行以后,将会独占它所用到的所有硬件资源,不得中断,直到执行结束为止。

第 i 个任务用单个 CPU,两个 CPU,单个 CPU 加 GPU,两个 CPU 加 GPU 运行所消耗的时间分别为 ai,bi,ci 和 di。

现在需要你计算出至少需要花多少时间可以把所有给定的任务完成。

输入格式

输入的第一行只有一个正整数 n,是总共需要执行的任务个数。

接下来的 n 行每行有四个正整数 ai,bi,ci,di,以空格隔开。

输出格式

输出只有一个整数,即完成给定的所有任务所需的最少时间。

数据范围

1≤n≤40
1≤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 完成。

暴力dfs 超时(4/12):

#include<iostream>
using namespace std;
const int N=45;
int n,flag[N],ans=0x3f3f3f3f;
struct node{
    int a,b,c,d;
}t[N];
void swap(int &a,int &b){
    int temp=a;a=b;b=temp;
}
void dfs(int cnt,int cpu1,int cpu2,int gpu){
    if(cnt==n+1){
        int res=max(max(cpu1,cpu2),gpu);
        if(res<ans){
            ans=res;
            return;
        }
    }
    for(int i=cnt;i<=n;i++){
        // 交换任务顺序
        swap(flag[cnt],flag[i]);
        // 尝试分配到CPU1
        dfs(cnt+1,cpu1+t[flag[cnt]].a,cpu2,gpu);
        // 尝试分配到CPU2
        dfs(cnt+1,cpu1,cpu2+t[flag[cnt]].a,gpu);
        // 尝试分配到GPU,同时更新CPU1和GPU
        int res=max(cpu1,gpu)+t[flag[cnt]].c;
        dfs(cnt+1,res,cpu2,res);
        // 尝试分配到GPU,同时更新CPU2和GPU
        res=max(cpu2,gpu)+t[flag[cnt]].c;
        dfs(cnt+1,cpu1,res,res);
        // 尝试同时分配到CPU1、CPU2和GPU
        res=max(max(cpu1,cpu2),gpu)+t[flag[cnt]].d;
        dfs(cnt+1,res,res,res);
        // 恢复任务顺序
        swap(flag[cnt],flag[i]);
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) flag[i]=i;
    for(int i=1;i<=n;i++){
        cin>>t[i].a>>t[i].b>>t[i].c>>t[i].d;
        t[i].d=min(t[i].d,t[i].b);
    }
    dfs(1,0,0,0);
    cout<<ans<<endl;
    return 0;
}

考试时候可以写出暴力解法对我来说就很好了...

动态规划 AC:

#include<iostream>
#include<cstring>
using namespace std;
const int N=50, M=210;// N为任务数量上限,M为资源使用上限
int n;
// f[u][i][j][k]表示第u个任务完成后,CPU1使用i个单位时间,CPU2使用j个单位时间,GPU使用k个单位时间的最小完成时间
int f[2][M][M][M];
// c[i][0]为CPU1需求,c[i][1]为GPU需求,c[i][2]为CPU2需求
int c[N][3];
int main(){
    cin >> n;
    // 初始化变量m和m2,用于计算所有任务CPU1需求的总和
    int m=0, m2=0;
    for(int i=1;i<=n;i++){
        int x, y, z, t;
        cin >> x >> y >> z >> t;
        // 存储每个任务的资源需求
        c[i][0] = x, c[i][1] = z, c[i][2] = min(y, t);
        // 累加CPU1的总需求
        m += x;
        // 根据任务编号奇偶性累加CPU1的需求
        if(i % 2) m2 += x;
    }
    // 计算最大可能的CPU1需求,用于确定动态规划数组的大小
    m = max(m2, m - m2);
    // 初始化动态规划数组f,表示初始状态下的完成时间为无穷大
    memset(f, 0x3f, sizeof f);
    // 设置初始状态,没有执行任何任务时的完成时间为0
    f[0][0][0][0] = 0;
    // 遍历每个任务
    for(int u=1;u<=n;u++)
        // 遍历CPU1的可能使用时间
        for(int i=0;i<=m;i++)
            // 遍历CPU2的可能使用时间
            for(int j=i;j<=m;j++)
                // 遍历GPU的可能使用时间
                for(int k=0;k<=j;k++) // GPU不能单独使用,因此k不能大于j
                {  
                    // 获取当前状态下的完成时间
                    int &v = f[u & 1][i][j][k];
                    // 获取当前任务的资源需求
                    int x = c[u][0], y = c[u][1], z = c[u][2], t = (u - 1) & 1;
                    // 尝试不同的分配方案
                    v = f[t][i][j][k] + z;  // 模式3:任务分配给GPU
                    if(i >= x) v = min(v, f[t][min(i - x, j)][max(i - x, j)][k]);  // 模式1:任务分配给CPU1
                    if(j >= x) v = min(v, f[t][min(j - x, i)][max(j - x, i)][k]);  // 模式1:任务分配给CPU2
                    if(i >= y && k >= y) v = min(v, f[t][i - y][j][k - y]);  // 模式2:任务同时分配给CPU1和GPU
                    if(j >= y && k >= y) v = min(v, f[t][i][j - y][k - y]);  // 模式2:任务同时分配给CPU2和GPU
                }
    int ans = 0x3f3f3f3f;
    for(int i=0;i<=m;i++)
        for(int j=i;j<=m;j++)
            for(int k=0;k<=j;k++)
                ans = min(ans, f[n & 1][i][j][k] + max(max(i, j), k));
    cout << ans << endl;;
    return 0;
}
  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值