源代码:ACM/OpenjudgeNow/Codeforces at master · abmcar/ACM (github.com)
更好的阅读体验: Codeforces Round #787 (Div. 3) 全题解 - Abmcar’s 茶水间
Problem - A - Codeforces
大致题意:
思路:
可以知道缺少的狗粮和猫粮分别为
m i n ( 0 , a − x ) min(0,a-x) min(0,a−x)和 m i n ( 0 , b − y ) min(0,b-y) min(0,b−y) 判断通用粮是否满足缺口即可
代码:
void solution()
{
int a, b, c, x, y;
cin >> a >> b >> c >> x >> y;
if (min(0LL, a - x) + min(0LL, b - y) + c >= 0)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
Problem - B - Codeforces
大致题意:
思路:
我们知道显然 a n a_n an不减少是最优的情况
倒叙遍历减少显然是最小操作的情况
当减少到0还不递增时数组无法生成为严格递增
代码:
void solution()
{
int n;
cin >> n;
vector<int> nums(n);
for (int &it : nums)
cin >> it;
int ans = 0;
int lastNum = 1e12;
for (int i = n - 1; i >= 0; i--)
{
while (nums[i] >= lastNum)
{
ans++;
nums[i] = nums[i] / 2;
if (nums[i] == 0)
break;
}
if (nums[i] >= lastNum)
{
cout << "-1" << endl;
return;
}
lastNum = nums[i];
}
cout << ans << endl;
}
Problem - C - Codeforces
大致题意:
思路:
题目有一点绕,但值得注意的是有且仅有一个小偷,我们要做的只是判断出谁可能是小偷
我们可以想一下什么情况下第i个人才可能是小偷?
答案很简单 小偷偷画前的所有人应该见过或者不记得有画,偷画后所有人应该没见到画或者不记得
也就是说,他前面的人没有说画丢了且后面的人都没有说还有画时,这个人才可能偷画
代码:
void solution()
{
string s;
cin >> s;
int n = s.size();
s = '+' + s;
vector<bool> aft1(n + 2), pre0(n + 2);
int ans = 0;
for (int i = n; i >= 1; i--)
aft1[i] = aft1[i + 1] or (s[i] == '1');
for (int i = 1; i <= n; i++)
pre0[i] = pre0[i - 1] or (s[i] == '0');
for (int i = 1; i <= n; i++)
if (!pre0[i - 1] and !aft1[i + 1])
ans++;
cout << ans << endl;
}
Problem - D - Codeforces
大致题意:
思路:
不难得知,树有多少个儿子,就有多少条路径
我们只需要从根dfs一遍即可找到路径
因此我们先输出路径数量,即叶子数量
之后dfs(root)
每当dfs到叶子节点时,我们输出当前路径并且清空
代码:
void solution()
{
int n;
cin >> n;
vector<int> father(n + 1), g[n + 1];
int root = -1;
for (int i = 1; i <= n; i++)
{
cin >> father[i];
if (i == father[i])
root = i;
}
for (int i = 1; i <= n; i++)
{
if (root == i)
continue;
g[father[i]].push_back(i);
}
int leaf = 0;
for (int i = 1; i <= n; i++)
if (g[i].empty())
leaf++;
vector<int> ans;
function<void(int)> dfs = [&](int nowNode) {
ans.push_back(nowNode);
// cout << nowNode << endl;
if (g[nowNode].size() == 0)
{
cout << ans.size() << endl;
for (int it : ans)
cout << it << " ";
cout << endl;
ans.clear();
return;
}
for (int nextNode : g[nowNode])
dfs(nextNode);
};
cout << leaf << endl;
dfs(root);
cout << endl;
}
Problem - E - Codeforces
题目大意:
思路:
一个非常有迷惑性的题,k给的相当大
但实际上,由于每次替换的是当前字符串中的所有相同字母,因此只需要’z’-'a’的操作数即可达到最优
我们记录哪些字母已经被替换过了
之后从头开始,每次遇到没被替换过的字母尽可能的替换,之后记录下来本次替换的范围即可
代码:
void solution()
{
int n, k;
string s;
cin >> n >> k >> s;
map<char, char> mp;
mp['a'] = 'a';
for (char &it : s)
{
if (mp.count(it))
{
it = mp[it];
continue;
}
char oriChar = it;
int tmp = 0;
while (k > 0 and !mp.count(it))
{
it--;
k--;
tmp++;
}
char tmpChar = it;
if (!mp.count(it))
mp[it] = it;
it = mp[it];
while (tmpChar <= oriChar)
{
// cout << tmpChar << " " << it << endl;
mp[tmpChar++] = it;
}
}
cout << s << endl;
}
Problem - F - Codeforces
题目大意:
思路:
(龙龙送外卖)
我们知道所有点都需要去,且最终目的地是y,因此要做的就是尽可能减少来回跑的长度
我们可以先以x为root进行一遍dfs,以vis判断是否来过这个点
之后对于每个任务点 t a r g e t i target_i targeti若该点没有来过,则需要去一趟来一趟,ans+2,标记vis
接着while判断 f a t h e r [ t a r g e t ] father[target] father[target]直到 v i s [ t a r g e t ] = = t r u e vis[target] == true vis[target]==true
最后 把y也加入任务点 再减去x到y的距离即为最终答案(在y不用返回)
代码:
void solution()
{
int n, k, x, y;
cin >> n >> k;
cin >> x >> y;
vector<int> g[n + 1], target(k + 1), father(n + 1), deep(n + 1, 1e9);
vector<bool> vis(n + 1);
for (int i = 1; i <= k; i++)
cin >> target[i];
for (int i = 2; i <= n; i++)
{
int t1, t2;
cin >> t1 >> t2;
g[t1].push_back(t2);
g[t2].push_back(t1);
}
function<void(int, int)> dfs = [&](int nowNode, int fatherNode) {
//
for (int nextNode : g[nowNode])
{
if (nextNode == fatherNode)
continue;
father[nextNode] = nowNode;
deep[nextNode] = deep[nowNode] + 1;
dfs(nextNode, nowNode);
}
};
deep[x] = 0;
dfs(x, -1);
vis[x] = true;
int ans = 0;
target[0] = y;
for (int targetNode : target)
{
while (!vis[targetNode])
{
ans += 2;
vis[targetNode] = true;
targetNode = father[targetNode];
}
}
ans = ans - deep[y];
cout << ans << endl;
}
Problem - G - Codeforces
题目大意:
代码:
int main()
{
int n, m;
cin >> n >> m;
vector<int> nums(n + 1), preAns(n + 1);
for (int i = 1; i <= n; i++)
cin >> nums[i];
for (int i = 1; i <= n; i++)
preAns[i] = preAns[i - 1] + nums[i];
vector dp(n + 1, vector<int>(m + 1, 1e9));
// dp[i][j] -> 前i个位置用了j个盘子之后达成递增的最小操作数
// dp[i][j] = min{dp[i-1][j-k] + abs(j-k-preAns[i-1])}
dp[0][0] = 0;
// 下一个位置放的盘子数
for (int nextValue = m; nextValue >= 0; nextValue--)
// 当前已经用的盘子数
for (int nowValue = 0; nowValue <= m - nextValue; nowValue++)
// 当前位置
for (int i = 1; i <= n; i++)
/* 前面i-1个已经是递增的 代价已经在dp[i - 1][nowValue]里面了
abs算的是前i - 1个整体少的或者多的部分需要从i这里过的操作数 */
dp[i][nextValue + nowValue] =
min(dp[i][nextValue + nowValue], dp[i - 1][nowValue] + abs(nowValue - preAns[i - 1]));
cout << dp[n][m] << endl;
return 0;
}