小结:
题目整体难度不大,前四题还好写,第五题hard难度的dp,思路确实想不到,写了三个小时,第三题模拟题不知道stringstream,写得很吃力,最后放弃了3,5题,得分300。有点菜继续加油。
题解:(一,二题简单无思路,三,五题代码参考自acwing)
题一:相反数
有 N𝑁 个非零且各不相同的整数。
请你编一个程序求出它们中有多少对相反数(a 和 −a 为一对相反数)。
输入格式
第一行包含一个正整数 N𝑁。
第二行为 N 个用单个空格隔开的非零整数,每个数的绝对值不超过 1000,保证这些整数各不相同。
输出格式
只输出一个整数,即这 N 个数中包含多少对相反数。
数据范围
1≤N≤500
代码:
#include<bits/stdc++.h> using namespace std; const int N=1001; int g[N]; int ans[N]; int n; int main() { cin>>n; for(int i=1;i<=n;i++){ cin>>g[i]; if(g[i]>0){ ans[g[i]]++; }else if(g[i]<0){ ans[-g[i]]++; } } int res=0; for(int i=1;i<=1000;i++){ if(ans[i]>1){ res++; } } cout<<res; }
题二: 窗口
在某图形操作系统中,有 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
代码:
#include<bits/stdc++.h> using namespace std; struct G{ int th; int x1,x2,y1,y2; }g[20]; int n,m; void solve() { cin>>n>>m; for(int i=1;i<=n;i++){ int a,b,c,d; cin>>a>>b>>c>>d; g[i].th=i; g[i].x1=a,g[i].y1=b,g[i].x2=c,g[i].y2=d; } for(int i=1;i<=m;i++){ G tex; bool flag=0; int x,y; cin>>x>>y; for(int j=n;j>0;j--){ if(x>=g[j].x1&&x<=g[j].x2&&y>=g[j].y1&&y<=g[j].y2){ flag=1; cout<<g[j].th<<endl; tex=g[j]; for(int k=j;k<n;k++){ g[k]=g[k+1]; } g[n]=tex; break; } } if(!flag){ cout<<"IGNORED"<<endl; } } } int main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); int t=1; while(t--){ solve(); } return 0; }
题三: 命令行选项
这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。
如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。
例如,
ab:m:
表示该程序接受三种选项,即-a
(不带参数),-b
(带参数),以及-m
(带参数)。命令行工具的作者准备了若干条命令行用以测试你的程序。
对于每个命令行,你的工具应当一直向后分析。
当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。
命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。
输入格式
输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。
格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。
输入的第二行是一个正整数 N,表示你需要处理的命令行的个数。
接下来有 N行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。
输出格式
输出有 N 行。其中第 i 行以
Case i:
开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。如果一个选项在命令行中出现了多次,只输出一次。
如果一个带参数的选项在命令行中出现了多次,只输出最后一次出现时所带的参数。
数据范围
1≤N≤20,
对于每组数据,所有命令行工具的名字一定相同,且由小写字母构成。思路:
模拟题,没有思维难度,主要是细节,用到了stringstream的方法读取字符串。
采用了两个bool数组记录不同方法。
代码:
#include <iostream> #include <cstring> #include <algorithm> #include <sstream> using namespace std; const int N = 30; bool st1[N], st2[N]; // st1 存放的是无参数选项 st2 存放的是有参数选项 string ans[N]; // 该字母后存放的参数 int n; int main() { string str; cin >> str; // 处理第一行的数据 进行标注(之后存在这个点才会输出) for (int i = 0; i < str.size(); i ++) { if (str[i+1] == ':' && i + 1 < str.size()) { st2[str[i] - 'a'] = true; i ++; // 注意 } else st1[str[i] - 'a'] = true; } cin >> n; getchar(); // 除去输入n后边的换行 注意 for (int C = 1; C <= n; C ++) { printf("Case %d:", C); //注意 getline(cin,str); stringstream ssin(str); vector<string> ops; while (ssin >> str) ops.push_back(str); for (int i = 0; i < 26; i ++) ans[i].clear();//注意 for (int i = 1; i < ops.size(); i ++) // 从1开始做 ls不用管 { //不满足条件break if (ops[i][0] != '-' || ops[i][1] < 'a' || ops[i][1] > 'z' || ops[i].size() != 2) break; int k = ops[i][1] - 'a'; if (st1[k]) ans[k] = '/'; else if(st2[k] && i + 1 < ops.size()) ans[k] = ops[i+1], i ++; // 注意 else break; } for (int i = 0; i < 26; i ++) { if (ans[i].size()) { cout << " -" << char (i + 'a'); //注意 if (st2[i]) cout << " " << ans[i]; } } cout << endl; } return 0; }
题四:无线网络
目前在一个很大的平面房间里有 n个无线路由器,每个无线路由器都固定在某个点上。
任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
除此以外,另有 m 个可以摆放无线路由器的位置。
你可以在这些位置中选择至多 k 个增设新的路由器。
你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。
请问在最优方案下中转路由器的最少个数是多少?
输入格式
第一行包含四个正整数 n,m,k,r。
接下来 n行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线路由器在 (xi,yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi,yi) 点处可以增设一个路由器。
输入中所有的坐标的绝对值不超过 1e8,保证输入中的坐标各不相同。
输出格式
输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1个路由器到第 2 个路由器最少经过的中转路由器的个数。
思路:
采用bfs,但是不能用二维数组存点,会超内存。采用邻接表的方式存点,将距离不超过r的点之间建立无向边。采用拆点的思路用二维数组dis[i][j]存值表示i点在过j个新路由器的最短距离。
代码:
#include<bits/stdc++.h> using namespace std; const int N = 210, M = N * N; typedef pair<int, int> PII; int h[N], e[M], ne[M], idx; struct P { long long x, y; }p[N]; int dis[N][N]; int n, m, k, r; bool st[N][N]; int cnt; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } void bfs() { memset(dis, 0x3f, sizeof(dis)); queue<PII> q; q.push({ 1,0 }); dis[1][0] = 0; while (q.size()) { auto t = q.front(); int x = t.first, y = t.second; q.pop(); if (st[x][y]) { continue; } st[x][y] = true; for (int i = h[x];i != -1;i = ne[i]) { y = t.second; int j = e[i]; if (j > n) { y++; if (y <= k) { if (dis[j][y] > dis[x][y - 1] + 1) { dis[j][y] = dis[x][y - 1] + 1; q.push({ j,y }); } } } else { if (dis[j][y] > dis[x][y] + 1) { dis[j][y] = dis[x][y] + 1; q.push({ j,y }); } } } } } void solve() { memset(h, -1, sizeof(h)); cin >> n >> m >> k >> r; for (int i = 1;i <= n;i++) { cin >> p[i].x >> p[i].y; } for (int i = 1;i <= m;i++) { cin >> p[n + i].x >> p[n + i].y; } for (int i = 1;i <= n + m;i++) { for (int j = i + 1;j <= n + m;j++) { long long o = (p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y); if (o <= (long long)r * r) { add(i, j); add(j, i); } } } bfs(); int ans = 0x3f3f3f3f; for (int i = 1;i <= k;i++) { ans = min(ans, dis[2][i]); } cout << ans-1; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int t = 1; while (t--) { solve(); } return 0; }
题五: 任务调度
有若干个任务需要在一台机器上运行。
它们之间没有依赖关系,因此可以被按照任意顺序执行。
该机器有两个 CPU 和一个 GPU。
对于每个任务,你可以为它分配不同的硬件资源:
- 在单个 CPU 上运行。
- 在两个 CPU 上同时运行。
- 在单个 CPU 和 GPU 上同时运行。
- 在两个 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思路:
首先将四个调度可以分为三种,可以看到每种调度都需要CPU,所以,可以将调度2、4合并,记为新的调度3,
且可以放在其它所有任务之后执行,不会影响整体时间(这种占据所有资源,所以直接将占用时间累加到总时间上即可),
而调度1:占用一个CPU,调度2:占用一个CPU和一个GPU,调度1和调度1,调度1和调度2均不会冲突,
调度总时间直接加上调度的最大时间即可,但是调度2和调度2会由于争夺GPU而冲突,
假设使用CPU1总时间为i,CPU2总时间为j,GPU总时间为k
调度的总时间的最小值依旧为max(i,j,k),两个CPU错峰使用GPU即可,不会对最少总时间有影响
尝试使用DP求解
f[u][i][j][k]表示前u个任务,CPU1使用总时间i,CPU2使用总时间j,GPU使用总时间k时,
调度3占用时间的最小值,状态转移可以通过任务u的调度选择来完成代码:
#include <bits/stdc++.h> using namespace std; const int N = 50, M = 210, INF = 0x3f3f3f3f; int n; int f[2][M][M][M]; int c[N][3]; int main() { cin >> n; 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); m += x; if (i % 2) m2 += x; } m = max(m2, m - m2); memset(f, 0x3f, sizeof f); f[0][0][0][0] = 0; for (int u = 1; u <= n; u ++) for (int i = 0; i <= m; i ++) for (int j = i; j <= m; j ++) for (int k = 0; k <= j; k ++) //k<=j (GPU不能单独使用) { 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 if (i >= x) v = min(v, f[t][min(i - x, j)][max(i - x, j)][k]); //模式1 if (j >= x) v = min(v, f[t][min(j - x, i)][max(j - x, i)][k]); //模式1 if (i >= y && k >= y) v = min(v, f[t][i - y][j][k - y]); //模式2 if (j >= y && k >= y) v = min(v, f[t][i][j - y][k - y]); //模式2 } int res = INF; for (int i = 0; i <= m; i ++) for (int j = i; j <= m; j ++) for (int k = 0; k <= j; k ++) res = min(res, f[n & 1][i][j][k] + max(max(i, j), k)); cout << res; return 0; }