题目链接:点这里.
这套题目还是很有质量的,都是暴力过不了的题,但是觉得牛客网数据还是有点水,很多人都暴力过了……,第一题要二分,而且选对数据结构对代码的复杂度有很大的减低;第二题简单的动态规划,处理好非负数就好了.
第一题:火眼金睛
题目:
现在我们需要查出一些作弊的问答社区中的ID,作弊有两种:1.A回答了B的问题,同时B回答了A的问题。那么A和B都是作弊。2.作弊ID用户A和作弊ID用户B同时回答了C的问题,那么C也是作弊。已知每个用户的ID是一串数字,一个问题可能有多个人回答。
解析:
首先要考虑ID可能是离散的,所以要双向 map ,不过后台数据貌似是连续的;
其次先求出第一种方式作弊的ID,这里要用到二分,选用的是STL中的 set ,自动二分查找而且不用考虑重复的问题,然后用选出的ID不断选出第二种方式作弊的ID;
最后再把连续的ID映射回原ID,这里也用 set ,自动排序.
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
while (~scanf("%d", &n)) {
map<int, int> mp, toSrc;
int ids = 0;
vector<set<int> > arr(n + 1);
for (int i = 0; i < n; i++) {
int id_top, numOfAnswer;
scanf("%d%d", &id_top, &numOfAnswer);
if (mp.find(id_top) == mp.cend())
mp[id_top] = ids++, toSrc[mp[id_top]] = id_top;
int question = mp[id_top];
for (int j = 0; j < numOfAnswer; j++) {
int id;
scanf("%d", &id);
if (mp.find(id) == mp.cend())
mp[id] = ids++, toSrc[mp[id]] = id;
if (mp[id] == question)
continue;
arr[question].insert(mp[id]);
}
}
set<int> ans;
for (int i = 0; i < n; i++) {
for (auto it = arr[i].begin(); it != arr[i].end(); ++it) {
if (arr[*it].find(i) != arr[*it].cend()) {
ans.insert(i);
ans.insert(*it);
}
}
}
while (true) {
bool isOk = true;
for (int i = 0; i < n; i++) {
if (ans.find(i) != ans.cend())
continue;
for (auto it1 = arr[i].begin(); it1 != arr[i].end(); ++it1) {
auto it2 = it1;
for (++it2; it2 != arr[i].end(); ++it2) {
if (ans.find(*it1) != ans.cend() && ans.find(*it2) != ans.cend()) {
isOk = false;
ans.insert(i);
goto F;
}
}
}
F:;
}
if (isOk)
break;
}
set<int> theAns;
for (auto it = ans.begin(); it != ans.end(); ++it)
theAns.insert(toSrc[*it]);
printf("%d\n", theAns.size());
bool isFirst = true;
for (auto it = theAns.begin(); it != theAns.end(); ++it) {
if (!isFirst)
putchar(' ');
isFirst = false;
printf("%d", *it);
}
if (!isFirst)
puts("");
}
return 0;
}
第二题:矩阵元素相乘
题目:
A[n,m] 是一个 n 行
m 列的矩阵, a[i,j] 表示 A 的第i 行 j 列的元素,定义x[i,j] 为 A 的第i 行和第 j 列除了a[i,j] 之外所有元素(共 n+m−2 个)的乘积,即 x[i,j]=a[i,1]∗a[i,2]∗...∗a[i,j−1]∗...∗a[i,m]∗a[1,j]∗a[2,j]...∗a[i−1,j]∗a[i+1,j]...∗a[n,j] ,现输入非负整形的矩阵 A[n,m] ,求 MAX(x[i,j]) ,即所有的 x[i,j] 中的最大值。
解析:
暴力肯定超时,我们预处理出两个方向的总体乘积,但是如果矩阵中有0,那么预处理出来的乘积就不对,因此,我们再遇到0的时候乘1,然后在预处理出每行每列有多少个0.
维护最大值时,如果发现某一行或某一列有两个以上的0,那么这个 [i,j] 就不用计算了,如果当前元素是0,且这一行和这一列只有一个0,那么就可以用这行的积乘这列的积再除以当前元素的平方来维护最大值.
由于事先不知道数据范围的大小,我又不想动态分配内存了,因此选用了STL中的 vector .
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int n, m;
while (cin >> n >> m) {
vector<vector<int> > matrix(n + 1, vector<int>(m + 1));
vector<vector<vector<LL> > > dp(2, vector<vector<LL> >(n + 1, vector<LL>(m + 1, 1)));
vector<int> x(n + 1, 0), y(m + 1, 0);
for (int i = 1; i <= n; i++) {
LL sum = 1;
for (int j = 1; j <= m; j++) {
int xx;
cin >> xx;
matrix[i][j] = xx;
if (!xx) {
xx = 1;
x[i]++;
y[j]++;
}
dp[0][i][j] = dp[0][i][j] * (sum *= xx);
}
for (int j = 1; j <= m; j++) {
if (!matrix[i][j])
dp[1][i][j] = dp[1][i - 1][j];
else
dp[1][i][j] = dp[1][i - 1][j] * matrix[i][j];
}
}
LL ans = 0;
for (int i = 1; i <= n; i++) {
if (x[i] > 1)
continue;
for (int j = 1; j <= m; j++) {
if (!matrix[i][j] && y[j] > 1)
continue;
LL tag = matrix[i][j] == 0 ? 1 : matrix[i][j];
ans = max(ans, dp[0][i][m] * dp[1][n][j] / (tag * tag));
}
}
cout << ans << endl;
}
return 0;
}