A. Food for Animals
第一题…
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
vector<int>v(N);
void solve() {
ll a, b, c, x, y;
cin>>a>>b>>c>>x>>y;
if(min(a - x, 0ll) + min(b - y, 0ll) + c >= 0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}
B. Make It Increasing
题意描述:可以对数组中的任意元素除2(向下取整)。问:至少多少步,可以使数组严格递增。如果不可能严格递增,输出-1。
题意分析:从后往前(倒数第二个元素开始),如果大于等于下标加一的元素则进行除2,如果当前元素已经为0且仍然大于等于后一个元素则不可能。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
vector<int>v(N);
void solve() {
int n, ok = 1, ans = 0;
cin>>n;
vector<int> v(n);
for(int i=0;i<n;i++) {
cin>>v[i];
}
for(int i=n-2;i>=0;i--) {
while(v[i] == 0 && v[i] >= v[i + 1]) { // v[i] == 0时可以退出调整
v[i] /= 2; // 调整操作
ans++;
}
if(v[i] >= v[i + 1]) ok = 0; // 特判不可能情况
}
if(!ok)
cout<<-1<<endl;
else
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}
C. Detective Task
题意描述:一开始房间有一幅画,最后房间画丢了。画主人询问每一个参观者,得到三种回答:0
进入前画已经没了、1
进入前画还在和?
记不得了。其中,除小偷外每个人都说真话,小偷畅所欲言。问可能偷画的有几个人?
题意分析:首先,第一个说0和最后一个说1的人一定撒谎了,输出1即可。其次,第一个说0的人一定是有问题的,无论他是不是小偷(因为普通观赏着不能撒谎),此时左边最靠近他的说1的人和他是有矛盾的,或者之前没人说1则从头算起都有可能是小偷。最后,没人说零的话,最后一个说1的人及之后都有可能是小偷,在这种情况下连说1的都没有,则全部都可疑。
针对第三种情况:最后一个说1的人及之后都有可能是小偷。因为此情况下没人说0,则之前的人要么说’1’要么说’?',假设之前画已经被偷了则当前人说谎了,当前画没被偷则当前人也是最后一个接触者,因此撒谎者只有可能是说1的人及之后。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
void solve() {
string s;
cin>>s;
int len = s.length(), ans = 0, ind = 0;
for(int i=0;i<len;i++) {
if(s[i] == '0') {
ans = i - ind + 1; // ans存储第二种情况答案
break;
}
if(s[i] == '1') ind = i; // 寻找最靠近'0'的'1',如果没有出现'0'则会标记最后一个'1'
}
if(s[0] == '0' || s[len - 1] == '1') //第一种情况
cout<<1<<endl;
else if(ans) // 第二种情况
cout<<ans<<endl;
else // 最后
cout<<len - ind<<endl;
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}
D. Vertical Paths
题意描述:将一颗数按照自顶向下的方式拆分,要求拆分路径无重合节点不重复,且拆分数最少。
题意分析:题目中拆分数最少,其实可以转化为一个叶子节点一条路径,没有比这更少的拆分了。
bfs嵌套dfs做法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
int n, rt;
queue<int>q;
vector<int>tree[N];
vector<vector<int>>ans; //存储答案的数组
void dfs(int x)
{
// 注意从1开始,因为对0号孩子进行dfs
// 而0号孩子的兄弟是要作为子树的根节点的
ans.back().push_back(x);
for(int i=1;i<tree[x].size();i++) { // 兄弟节点加入队列
q.push(tree[x][i]);
}
if(tree[x].size())
dfs(tree[x][0]);
}
void solve() {
cin>>n;
ans.clear();
for(int i = 0; i <= n; i++) tree[i].clear();
vector<int> a(n + 1);
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i] == i) rt = i;
else { // 建树
tree[a[i]].push_back(i); //当前元素是i的父亲
}
}
q.push(rt);
while(!q.empty())
{
rt = q.front();
q.pop();
ans.push_back(vector<int>());
dfs(rt);
}
cout<<ans.size()<<endl;
for(auto v : ans) {
cout<<v.size()<<endl;
for(auto item : v) {
cout<<item<<" ";
}
cout<<endl;
}
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}
从叶子节点入手做法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
int n, rt;
int vis[N];
void solve() {
cin>>n;
vector<int> a(n + 1), lef;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i] == i) rt = i;
vis[a[i]] = 1;
}
for(int i=1;i<=n;i++) {// 确定叶子节点
if(!vis[i]) lef.push_back(i);
vis[i] = 0;
}
if(n == 1) lef.push_back(1);
vector<vector<int>> ans;
for(int i=0;i<lef.size();i++) {
int now = lef[i];
vector<int> v;
while(now && !vis[now]) {
v.push_back(now);
vis[now] = 1;
now = a[now];
}
if(v.size()) ans.push_back(v);
}
cout<<ans.size()<<endl;
for(auto v : ans) {
cout<<v.size()<<endl;
for(int i = v.size() - 1; i >= 0; i--) {
cout<<v[i]<<" ";
}
cout<<endl;
}
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}
E. Replace With the Previous, Minimize
题意描述:对一个只包含小写字母的字符串进行操作,每次可以让同一种字符减一。最终的到的字典序最小的串是?
题意分析:从第一个字符开始贪心(自减)。注意:大字符变为较小的字符时,它中间遇到的所有字符可以同之后串中出现的同一字符一起减;另外,一字符自减过程中的字符在之前被标记过(可以同之前同一字符一起自减),则不用自减。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
typedef pair<int, int> pii;
void solve() {
int n, k;
char vis[130] = {0}, ch[130] = {0};
string s;
cin>>n>>k>>s;
// 递减并标记所遇到的字符
for(auto c : s) while(c > 'a' && !vis[c] && k > 0) vis[c] = 1, c --, k --;
for(int i = 'a'; i <= 'z'; i++) {
for(int j = i; j >= 'a'; j--) { // 查看每个小写字母最小可以减为什么字母
ch[i] = j;
if(!vis[j]) break; // 退出位置很关键, 因为vis不标记最后退出的字符
}
}
for(auto &c : s) c = ch[c];
cout<<s<<endl;
}
int main()
{
ios::sync_with_stdio(false);
int t = 1;
cin>>t;
while (t--) {
solve();
}
return 0;
}