我在选拔赛中做对了10道题,9和12没对,后面最难的3道题也没对,但是没有开放补题。
第9题
这题是一个比较简单的图论题,关键在于题意的理解。朋友的朋友就是朋友,这意味着这张关系图中,假设标记为1的边为可行路径,那么在a和b没有直接敌对关系的情况下,只要a和b之间有可行路径,就说明a和b是朋友。但是题目中又说了a和b不能同时为朋友和敌人,所以如果a和b有敌对关系那么就不能是朋友了,这种情况就对应着“敌人,但有共同的朋友”。
考虑到这题的数据量比较小,不易TLE和MLE,只需要对询问的数据进行搜索即可,因为同时需要dfs和查询直接关系,所以同时用邻接表和邻接矩阵存图比较好,和其他的dfs一样,因为避免出现成环时的死循环情况,必须要使用vis数组。
#include <bits/stdc++.h>
using namespace std;
int arr[105][105]; //图的顶点
bool vis[105]; //存放每个点是否被访问
typedef pair<int, int> node;
vector<node> vec[105];
bool dfs(int s, int e) //友谊线能否到达对面
{
bool ret = 0;
if(s == e)
{
return 1;
}
for(auto x: vec[s])
{
int fir = x.first, sec = x.second; //人和关系
if(sec == 1 && !vis[fir]) //防止成环
{
vis[fir] = 1;
ret = dfs(fir, e);
vis[fir] = 0;
}
}
return ret;
}
int main()
{
int N, M, K;
cin >> N >> M >> K;
for(int i = 1; i <= M; ++i)
{
int a, b, c;
cin >> a >> b >> c;
vec[a].push_back(make_pair(b, c));
vec[b].push_back(make_pair(a, c));
arr[a][b] = arr[b][a] = c;
}
for(int i = 1; i <= K; ++i)
{
int a, b;
cin >> a >> b;
vis[a] = 1;
bool f = dfs(a, b);
vis[a] = 0;
if(f) //f不了就说明已经不是朋友了
{
if(arr[a][b] != -1)
{
cout << "No problem" << endl;
}
else
{
cout << "OK but..." << endl;
}
}
else
{
if(arr[a][b] != -1)
{
cout << "OK" << endl;
}
else
{
cout << "No way" << endl;
}
}
}
return 0;
}
此外我还有一种方法,我们不是学过弗洛伊德算法吗?还记得那三重循环吗?那我们就可以写出三重循环,通过这三重循环,一次加入一个点,判断每两个点之间是否有可行路径就可以了。这样子也不会遗漏。注意因为有朋友和敌人两种关系,所以要用两张图来分别存储“直接关系”和“是否有共同的朋友”两种关系就可以了。我正因为做的时候没考虑到这个所以就错了。看起来三重循环,实际上数据量小,不会超时,在询问量比较大的时候这种方法就会比较好了。这种办法比dfs的还要快呢!!!
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int arr[105][105], ak[105][105];
//arr用于存储两点间的直接关系,ak用于存储是否有共同的朋友
int main()
{
cin >> n >> m >> k;
for(int i = 1; i <= m; ++i)
{
int a, b, c;
cin >> a >> b >> c;
arr[a][b] = arr[b][a] = c;
if(c != -1)
ak[a][b] = ak[b][a] = c;
}
//类似弗洛伊德算法的思想,琼剧
for(int t = 1; t <= n; ++t)
{
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{ //双向图不用害怕
if(ak[i][t]==1 && ak[t][j]==1)
{
ak[i][j] = 1;
}
}
}
}
while(k--)
{
int a, b;
cin >> a >> b;
if(ak[a][b] == 1)
{
if(arr[a][b] >= 0)
{
cout << "No problem" << endl;
}
else
{
cout << "OK but..." << endl;
}
}
else
{
if(arr[a][b] >= 0)
{
cout << "OK" << endl;
}
else
{
cout << "No way" << endl;
}
}
}
return 0;
}
第12题
这题在第二组题中相对比较难一点,但是也是比较基础的题。这题考察的还是图论的基本知识和dfs递归查找。还有一个小技巧就是可以在dfs递归函数中额外添加一个参数来知道递归到了第几层,从而可以知道树/二叉树的深度。就是因为不会这个小技巧所以自己做的时候卡住。哦对了,输入一个人的父母id之后标记父母的性别。这是个隐含条件,我当时自作聪明没有标记结果错了就不知道哪里有问题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1E5+9;
struct person{
char sex;
int fa;
int ma;
} arr[maxn];
int vis[maxn];
int flag = 1;
void dfs(int f, int d) //起始下标
{
if(f >= 1 && d <= 5)
{
if(!vis[f])
{
vis[f] = 1;
dfs(arr[f].fa, d+1);
dfs(arr[f].ma, d+1);
}
else
{
flag = 0;
}
}
}
int find(int a, int b)
{
if(arr[a].sex == arr[b].sex)
{
return -1;
}
else //需要bfs
{
memset(vis, 0, sizeof(vis));
flag = 1;
dfs(a, 1), dfs(b, 1);
return flag;
}
}
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; ++i)
{
int a, b, c;
char d;
cin >> a >> d >> b >> c;
arr[a] = {d, b, c};
if(b != -1)
{
arr[b].sex = 'M'; //隐含条件
}
if(c != -1)
{
arr[c].sex = 'F'; //隐含条件
}
}
int q;
cin >> q;
while(q--)
{
int a, b;
cin >> a >> b;
switch(find(a, b))
{
case -1:
cout << "Never Mind" << endl;
break;
case 0:
cout << "No" << endl;
break;
case 1:
cout << "Yes" << endl;
break;
}
}
return 0;
}
还有几个因为错误罚时的题,这里我也写出来。
第六题
真没想到自己在hello world级别的题上面还能犯错。首先string类的substr函数常用第一个参数是开始的下标,第二个参数是字串的长度。其次这道题只说了A和B在1-1000之间,并没有说结果也一定在1-1000之间。到底还是语文限制了我的学习。
#include <bits/stdc++.h>
using namespace std;
void printnum(int v, int l, int r)
{
if(v >= l && v <= r)
{
printf("%d", v);
}
else{
putchar('?');
}
}
int main(){
string str, a, b;
getline(cin, str);
int s, len = str.length();
for(s = 0; s < len; ++s)
{
if(str[s] == ' ')
{
break;
}
}
a = str.substr(0, s), b = str.substr(s+1);
// cout << a << "_" << b << endl;
int va=0, vb=0;
for(auto x:a)
{
if(isdigit(x))
{
va = va*10+x-'0';
}
else
{
va = 0;
break;
}
}
for(auto x:b)
{
if(isdigit(x))
{
vb = vb*10+x-'0';
}
else
{
vb = 0;
break;
}
}
int vc;
if(va > 0 && va < 1001 && vb > 0 && vb < 1001)
{
vc = va+vb;
}
else{
vc = 0;
}
printnum(va, 1, 1000), printf(" + "), printnum(vb, 1, 1000),
printf(" = "), printnum(vc, 1, INT_MAX);
return 0;
}
第十题
这题被罚时五次。首先就是一个sort函数的使用排序题。按字母序排序是啥意思?其实只要字符串比较大小就可以。还有的成绩>=60就可以及格,不是>60,而且选入名人堂的人不一定成绩都及格了,有可能是瘸子里面挑将军的。又是语文限制了我的学习,梅开二度。
#include <bits/stdc++.h>
using namespace std;
int n, g, k, sum;
struct stu
{
int first;
string second;
};
int main()
{
cin >> n >> g >> k;
vector<stu> arr(n+1);
for(int i = 1; i <= n; ++i)
{
cin >> arr[i].second >> arr[i].first;
if(arr[i].first >= g)
{
sum += 50;
}
else if(arr[i].first >= 60)
{
sum += 20;
}
}
cout << sum << endl;
sort(arr.begin()+1, arr.end(), [](const stu&s1, const stu&s2)
{
if(s1.first != s2.first)
{
return s1.first > s2.first;
}
else
{
return s1.second < s2.second;
}
});
for(int i = 1, j = 1; ; i++)
{
if(arr[i].first == arr[j].first)
{
}
else
{
j = i;
}
if(j > k)
{
break;
}
cout << j << ' ' << arr[i].second << ' ' << arr[i].first << endl;
}
}