本专栏主要为了巩固基础算法,会把比赛中觉得比较好的题目拿过来整理一下。
说一下牛客周赛85感想:abc签到题,def提高题,其中d题比较难,当然我只做了签到题。虽说题目是博弈论,但是本质上考的是贪心场,f没做出来是因为不熟悉树的遍历,但同时也感受到了前中后序遍历的魅力(因为你只需要在递归函数主体内部稍加修改就可以做到好多事情)。
一、F-小紫的树上染色_牛客周赛 Round 85
题⽬来源:牛客周赛 Round 85
难度系数: ★★★
【解题】:主要用到的算法:二分,贪心,搜索,前缀和。
🖥️code:
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, k;
// 二分
int mid, need;
// 用于存树
vector<int> edges[N];
int st[N];
int f[N]; // 存储以i结点为根节点的树红色连通块数量
void dfs(int u)
{
st[u] = true;
for(auto v : edges[u])
{
if(!st[v])
{
dfs(v);
// 到达叶子结点
f[u] += f[v];
}
}
if(f[u] + 1 > mid)
{
need++;
f[u] = 0;
}
else f[u]++;
}
bool check(int mid)
{
need = 0;
memset(st, 0, sizeof st);
memset(f, 0, sizeof f);
dfs(1);
return need <= k;
}
int main()
{
cin >> n >> k;
for(int i = 1; i < n; i++)
{
int x, y; cin >> x >> y;
edges[x].push_back(y);
edges[y].push_back(x);
}
// 二分连通块的最大值
int l = 0, r = n;
while(l < r)
{
mid = (l + r) >> 1;
if(check(mid)) r = mid; // 最大连通块还可以在小
else l = mid + 1;
}
cout << l << endl;
return 0;
}
二、E-小紫的线段染色_牛客周赛 Round 85
题⽬来源:牛客周赛 Round 85
难度系数: ★★
【解题一】:本题的思路并不难,难的是怎么用代码判断无解情况。
但是这个种思路过不了,下面是代码,有大神还请帮我看看。
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
using namespace std;
const int N = 1e5 + 10;
int n;
struct node{
int l, r, id;
}a[N];
int f[N * 4];
int ans[N];
// 离散化
int disc[N * 4], pos, cnt;
unordered_map<int, int> mp;
bool cmp(node& x, node& y)
{
if(x.l != y.l) return x.l < y.l;
else return x.r < y.r;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
int l, r; cin >> l >> r;
a[i].l = l; a[i].r = r; a[i].id = i;
disc[++pos] = l; disc[++pos] = l + 1;
disc[++pos] = r; disc[++pos] = r + 1;
}
sort(disc + 1, disc + 1 + pos);
for(int i = 1; i <= pos; i++)
{
int t = disc[i];
if(mp.count(t)) continue;
cnt++;
mp[t] = cnt;
}
for(int i = 1; i <= n; i++)
{
int l = mp[a[i].l], r = mp[a[i].r];
f[l]++; f[r + 1]--;
}
for(int i = 1; i <= cnt; i++)
{
f[i] += f[i - 1];
if(f[i] > 2)
{
cout << -1 << endl;
return 0;
}
}
sort(a + 1, a + 1 + n, cmp);
vector<int> purple;
int id = 0, r = -1;
for(int i = 1; i <= n; i++)
{
if(a[i].l <= r)
{
ans[a[i].id] = (ans[id] ^ 1);
}
if(a[i].r > r)
{
r = a[i].r;
id = a[i].id;
}
}
for(int i = 1; i <= n; i++)
{
if(ans[i]) purple.push_back(i);
}
if(purple.size() == 0) purple.push_back(1);
for(auto x : purple)
{
cout << x << " ";
}
return 0;
}
【解题二】:题中要的是红紫线段不相交,我们同样和上述一样先按照左端点排序,然后依次为其分组:如果新遍历的线段的左端点和最后一个红色线段的右端点没有交点,就将其染红;如果新遍历的线段的左端点和最后一个紫色线段的右端点没有交点,就将其染紫,都有交点说明无解。
这种方式避免的离散化的开销。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Segment
{
int l, r, id;
} seg[N];
vector<int> disc;
unordered_map<int, int> mp;
bool compareSegment(const Segment& a, const Segment& b) {
if (a.l != b.l) return a.l < b.l;
return a.r < b.r;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; ++i)
{
cin >> seg[i].l >> seg[i].r;
seg[i].id = i + 1;
}
// 按左端点排序
sort(seg, seg + n, compareSegment);
// 贪心分配红紫组
vector<int> purple;
int last_red = -1, last_purple = -1;
for (int i = 0; i < n; ++i) {
int l = seg[i].l;
int r = seg[i].r;
if (l > last_red)
{
last_red = r;
}
else if (l > last_purple)
{
last_purple = r;
purple.push_back(seg[i].id);
}
else
{
cout << -1 << endl;
return 0;
}
}
// 检查紫色是否非空
if (!purple.empty()) {
cout << purple.size() << '\n';
for (int id : purple) {
cout << id << ' ';
}
cout << endl;
return 0;
}
// 全都为红,输出任意一个方案
cout << 1 << endl << 1 << endl;
return 0;
}
三、D-小紫的优势博弈_牛客周赛 Round 85
题⽬来源:牛客周赛 Round 85
难度系数: ★★★
【解题】:
🖥️code:
注意这里判断的状态是在i + 1后面所以我们要倒着遍历看是否出现相同状态。
还有一点是精度一定要留意!!!。
#include <iostream>
#include <iomanip>
using namespace std;
const int N = 1e6 + 10;
string s;
int f[N]; // 表示前缀0 1的奇偶性
int mp[4]; // 表示这个状态是否出现
int n;
int main()
{
cin >> n >> s;
s = " " + s;
double num = 0;
int c0 = 0, c1 = 0;
mp[0] = 1;
for(int i = n; i > 1; i--)
{
// 预处理前缀状态
c0 ^= (s[i] == '0');
c1 ^= (s[i] == '1');
int state = ((c0 << 1) | c1);
f[i] = state;
num += mp[f[i]];
mp[state] = 1;// 为了快速查询是否存在此状态
}
// cout << num << endl;
double ans = num / n;
cout << fixed << setprecision(10) << ans << endl;
return 0;
}