Codeforces Round #787 (div 3) A~E

比赛题目链接
https://codeforces.com/contest/1675
A.Food for Animals(模拟)
题意
有a份狗粮,b份猫粮,c份万能粮,问是否能够满足所有x条狗和y只猫
分析
先检视x条的狗能不能把a份狗粮吃掉,若吃完了狗粮,就再去吃c份通用的,一旦通用变为负数,表示的无法满足;猫粮也是同理

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    cin >> t;
    while(t --)
    {
        int a,b,c,x,y;
        cin >> a >> b >> c >> x >> y;

        if(a >= x) ;
        else  c -= x - a;
        if(c < 0 ) 
        {
            cout << "NO" << endl;
            continue;
        }
        if(b + c >= y) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

B.Make It Increasing(暴力)
题意
一个数组,可以对其中任意一个数除2并向下取整,经过多少次操作,可以将数组变为严格递增序列(升序并且每个数字都不相等)。
分析
数组大小只有30,数据大小1e9也不会超过2^{30},暴力即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int n;

int main()
{
    int T; cin >> T;
    while (T --)
    {
        cin >> n;
        for (int i = 1; i <= n; i ++) 
            cin >> a[i];
        //cnt用来存储次数,flag用于标记是否可以实现
        int cnt = 0;
        int flag = 0;
        for (int i = n - 1; i >= 1; i --)
        {
            if (a[i] < a[i + 1])
                continue;
            while (a[i] >= a[i + 1])
            {
                a[i] /= 2;
                cnt ++;
                //如果除得不能再除了还是避免不了相等,那就标记一下
                if (a[i] == 0 && a[i] >= a[i + 1])
                {
                    flag = 1;
                    break;
                }
            }
        }
        //如果被标记,说明不符合题意,输出-1
        if (flag) puts ("-1");
        else cout << cnt << "\n";
    }
    return 0;
}

C. Detective Task(模拟 + 思维)
题意
一个字符串,包含0,1,?。判断几个人有嫌疑,0代表不在,1代表在,?代表不记得。除了小偷每个人必须说真话。
分析
一共4种情况,
有0有1:第一个0之前的1到0之间都有嫌疑;
有1没0:最后一个1后面的都有嫌疑;
没1有0:第一个0之前的都有嫌疑;
没1没0:每个人都有嫌疑。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		string s;
		cin >> s;
		//ans用来存储犯人数量,idx表示离现在遍历的位置最近的1的下标
		int ans = 0, idx = -1;
		for (int i = 0; i < s.size(); i++)
		{
			if (s[i] == '1')
			{
				idx = i;
			}
			else if (s[i] == '0')
			{
				//有1有0,中间的全部都是犯人
				if (idx != -1)
				{
					ans = i - idx + 1;
				}
				//有0没1,第一个0之前的都有嫌疑;
				else ans = i + 1;
				break;
			}
		}
		//重点:ans = 0并不意味着没有犯人,还有无1无0的情况
		if (ans == 0)
		{
			//无1无0,全是犯人
			if (idx == -1)
			{
				ans = s.size();
			}
			else ans = s.size() - idx;
		}
		cout << ans << endl;
	}
	return 0;
}

D.Vertical Paths(dfs + 链表)
题意
一棵树,将这棵树拆成一条一条链,最少能拆成多少条,并输出每条长度和每条点。
分析
一个破题想一天,不过难写是真的难写;
可以用链表来存以某个数为头结点的数,当某个数的头结点为他本身的时候我们把他设为根结点,用一个vector数组来存他的路径数组,用一个len来记录路径数组的长度,用st数组来记录每个点的状态;
从根结点开始搜,当搜的这个点的指针指向-1时说明他是这条路的最后一个数,所以路径条数++;
当不等于-1时,我们遍历以这个点为头结点的链表,当遍历到的点没有被搜过的时候就把他加入路径数组中,并改变他的状态,然后搜这个点。最后输出路径条数和每个路径数组中存的点即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int n;
const int N = 200005;
//链表三巨头
int h[N];
int e[N];
int ne[N];
// 当前每个点的状态
int st[N];
//当前链表指针 ,路径数组长度,
int idx, len, root;
vector<int> ans[N];
void into_it() 
{
    memset(h, -1, sizeof h);
    memset(st, 0, sizeof st);
    for (int i = 1; i <= n; i++) 
    {
        ans[i].clear();
    }
    idx = 0;
    //答案序列的长度
    len = 1;
    //根结点,当父节点等于自己的时候赋值给root
    root = -1;
}
void add(int a, int b) 
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
void dfs(int u) 
{
    //说明一条路遍历完了,路的条数++,返回
    if (h[u] == -1)
    {
        len++;
        return;
    }
    for (int i = h[u]; i != -1; i = ne[i]) 
    {
        int j = e[i];
        //当没有走过的时候
        if (st[j] == 0)
        {
            ans[len].push_back(j);
            st[j] = 1;
            dfs(j);
        }
    }

}
int main() 
{
    int t;
    cin >> t;
    while (t--) 
    {
        cin >> n;
        //初始化,清空上次查询残留
        into_it();
        for (int i = 1; i <= n; i++) 
        {
            int num;
            cin >> num;
            //当父节点等于本身的时候说明是根结点
            if (num == i)
            {
                root = num;
            }
            //否则就把i加入以num为头的链表中
            else
            {
                add(num, i);
            }
        }
        ans[len].push_back(root);
        st[root] = 1;
        dfs(root);
        len--;
        //输出路径条数
        cout << len << endl;
        for (int i = 1; i <= len; i++) 
        {
            //输出每条路径的长度
            int q = ans[i].size();
            cout << q << endl;
            //输出每条路径中存的点
            for (int j = 0; j < q; j++)
            {
                printf("%d ", ans[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

E.Replace With the Previous, Minimize(贪心)
题意
一个字符串,每次操作可以使一个字母循环替换成该字母的前一个(如c->b),消耗一个次数,但是其他字母再次替换到此处时无需额外扣取次数(如前面c->b后,后面的d->c->b时,只用扣取d->c的一个费用),在最多m次操作之后,求出字典序最小的字符串。
分析
如果一个字母x足够大,当它下降时,比它小的字母全部都可以下降到同样的位置;而不会被一起调整的字母要不就是过大,要不就是比调整后的字母小;但是如果一个字母x太大并且在x - k的范围内字母较少,那就直接舍弃掉,从比x小一点点的开始找;

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
char s[N];
int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int n, k; 
        cin >> n >> k;
        cin >> s;
        int maxm = 0;//一定要定义一个除了被大于k数字影响的最大下降数
        for (int i = 0; i < n; i++)
        {
            if (s[i] - 'a' > k)//如果下降k次到不了a
            {
                k = k - maxm;
                char max_line = s[i];//把当前最大值更新为该数
                char min_line = s[i] - k;//该数能下降的最小字母
                for (int j = 0; j < n; j++)
                    if (s[j] >= min_line && s[j] <= max_line) //在下降区间内的
                    s[j] = min_line;//全部变成该数变化后的字母;
                break;
            }
            maxm = max(maxm, s[i] - 'a');//如果不超过k,更新最大下降数
        }
        for (int i = 0; i < n; i++)
            if (s[i] <= 'a' + maxm)
            {
            //不是加k,因为当前所有到不了a的字母都已经被更新到最优解,这一项只是为了能到a的数全部变成a
                s[i] = 'a';
            }
        cout << s << endl;
    }
}
posted @ 2022-05-21 20:10  Zilliax  阅读(65)  评论(0)    收藏  举报