小红书开发笔试题解
总体还是比较易,70分钟ak..(代价是鸽了今天的acm预选赛)
4个题,都是现场ac代码一点没改,不怎么好看。
第一题,给出用户的关注,互相关注的人在一个圈子里,并且有传递性。问总计有几个圈子。
裸并查集,或者忽视单向边跑一遍图有几个连通块,都是线性(并查集是接近线性)的复杂度。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int u[1005];
void init(int size)
{
for (int i = 0; i < size; i++) u[i] = -1;
}
int find(int x)
{
if (u[x] < 0) return x;
u[x] = find(u[x]);
return u[x];
}
void mix(int x, int y)
{
if ((x = find(x)) == (y = find(y))) return;
if (u[x] < u[y])
u[x] += u[y], u[y] = x;
else
u[y] += u[x], u[x] = y;
}
int main()
{
int i, j;
int n;
while (~scanf("%d", &n))
{
init(n);
int temp;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
scanf("%d", &temp);
if (temp == 1)
mix(i, j);
}
int ans = 0;
for (i = 0; i < n; i++)
if (u[i] < 0)
ans++;
printf("%d\n", ans);
}
return 0;
} 第二题,字符串的处理,删除所有括号内的内容(可能嵌套但一定成对),然后有<的删除它和它的前一个字符(如果有的话) 按照题意模拟就行..
#include <bits/stdc++.h>
using namespace std;
#define LL long long
char s[10005];
char ss[10005];
int main()
{
int i, j;
while (~scanf("%s", s))
{
int count = 0;
j = 0;
for (i = 0; s[i]; i++)
{
if (s[i] == '(')
count++;
if (count == 0)
ss[j++] = s[i];
if (s[i] == ')')
count--;
}
j = 0;
for (i = 0; ss[i]; i++)
{
if (ss[i] == '<')
{
j--;
j = max(0, j);
continue;
}
ss[j++] = ss[i];
}
ss[j] = 0;
puts(ss);
}
return 0;
} 第三题,给一个数组,从中取出一些数,保证取出的数位置不连续,和最大,且和最大的里取到的数的数目最少。问和多少,取几个数。数据范围1000 肯定往动态规划上想..(都是正数的话或许贪心也可?)
dp[i][j]表示最后一个取i,总计取了j个时的总和,有dp[i][k + 1] = max(dp[i][k + 1], dp[j][k] + a[i]),j取0到i-2,k取0-i。
这是O(n^3)的复杂度,不一定能过,但是有几个剪枝:都是正数的话,为了总和最大不可能连续空3个或以上,j只有3种取值(忘记题目是不是说都是正数了..),不连续取k一定小于等于i/2+1。
其实O(n^3)就能过...
开始想着用la数组记录父节点,能少一维,没啥必要..就申请了初始化了但没用
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int dp[1005][1005], la[1005];
int a[1005];
int main()
{
int i, j, k;
int n;
while (~scanf("%d", &n))
{
memset(dp, 0, sizeof(dp));
memset(la, -1, sizeof(la));
for (i = 0; i < n; i++)
scanf("%d", a + i);
dp[0][1] = a[0];
dp[1][1] = a[1];
for (i = 2; i < n; i++)
for (j = 0; j < i - 1; j++)
for (k = 0; k <= i / 2 + 1; k++)
{
dp[i][k + 1] = max(dp[i][k + 1], dp[j][k] + a[i]);
}
int ma = 0, co = 1005;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
{
if (dp[i][j] > ma || (dp[i][j] == ma && j < co))
ma = dp[i][j], co = j;
}
printf("%d %d\n", ma, co);
}
return 0;
} 开始以为是nlogn的最长上升子序列,快写完才发现和顺序没有关系..(然而就算是最长上升子序列也不对)
不限复杂度就dp一下,n^2时间,但是1e6肯定8行。
考虑降维,对坐标的xy排序,这样每个点x值一定大于等于它前面所有的点x,就可以无视x值只看y值了。
然后按照dp的思想,dp[i]=max(dp[j])+1,j<i且j.y≤i.y。
所以维护一个数组a,a[i]表示当前最后一个点纵坐标为i的序列的最大长度。
需要logn的查询区间最大值和更新,套个线段树就行。
注意数组a大小不是n,是纵坐标最大值。(还好纵坐标给的小,不然害得套个哈希)
一个大坑,给的数据范围是大于0,然而有等于0的数据,导致运行错误..所以代码里对所有y值加一。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct poi { int x, y; }a[1000005];
struct { int l, r, n; }t[1000005 << 2];
void build(int l, int r, int i)
{
t[i].l = l, t[i].r = r, t[i].n = 0;
if (l == r)
return;
int mid = (l + r) >> 1;
build(l, mid, i << 1), build(mid + 1, r, i << 1 | 1);
}
void update(int i, int x, int k)
{
t[k].n = max(t[k].n, x);
if (t[k].l == t[k].r)
return;
int mid = (t[k].l + t[k].r) >> 1;
if (i <= mid)
update(i, x, k << 1);
else
update(i, x, k << 1 | 1);
}
int sea(int l, int r, int k)
{
if (t[k].l == l && t[k].r == r)
return t[k].n;
int mid = (t[k].r + t[k].l) >> 1;
if (r <= mid)
return sea(l, r, k << 1);
if (l > mid)
return sea(l, r, k << 1 | 1);
return max(sea(l, mid, k << 1), sea(mid + 1, r, k << 1 | 1));
}
int cmp(poi a, poi b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
int main()
{
int i, j, k;
int n;
while (~scanf("%d", &n))
{
int ma = 0;
for (i = 1; i <= n; i++)
{
scanf("%d%d", &a[i].x, &a[i].y);
a[i].y++;
ma = max(ma, a[i].y);
}
sort(a + 1, a + n + 1, cmp);
build(1, ma, 1);
for (i = 1; i <= n; i++)
{
int temp = sea(1, a[i].y, 1);
update(a[i].y, temp + 1, 1);
}
printf("%d\n", sea(1, ma, 1));
}
return 0;
} 