【蓝桥杯】每日练习 Day4

目录

前言

回文游戏

分析

代码

牛奶交换

分析

代码

最大限度的提高生产力

分析

代码


前言

幸运日!!!

遇到三个非常简单的题。几乎没费什么脑子就写出来了。

本来今天是不打算再写的,因为做完实验本身就已经十点了,而且实验过程并不愉快(一条双绞线裁了九次……不过最终主播在朋友的帮助下还是成功做出来了——皆大欢喜)。

【蓝桥杯】每日练习 Day4_第1张图片

还有回寝室的时候遇到一只野生哈基米,好可爱^_^

【蓝桥杯】每日练习 Day4_第2张图片

今日事今日毕。事不宜迟我们马上开始吧!


回文游戏

【蓝桥杯】每日练习 Day4_第3张图片


分析

首先看到(最优策略绝顶聪明)等词就能想到这是一道博弈论问题了,于是我们开始按照博弈论的思路去思考。

分析先手必败状态,很明显0先手必败状态

随后1~9的个位数都是回文数,可以直接拿完,所以说都是先手必胜状态

再之后是10,肯定是不可以直接拿完的,因为10不是一个回文数(题目说了前导零不属于回文数),所以只能拿个位数,而拿完个位数之后剩下的数也是一个个位数——进入先手必胜状态。随后对手拿一个个位数我们就输了。

所以10是一个先手必败状态,而因为前导零不属于回文数,所以所有以0结尾的数字都不是回文数(0除外)

0又是一个先手必败状态,所以是不是只要最后一个数字是0,那么他就是一个先手必败状态呢?反之只要结尾不是0他就是先手必胜状态。

很显然是正确的,为什么?

老规矩,先证明所有的先手必胜状态都能转换成先手必败。

很显然这也是成立的,为什么?

因为个位数一定是回文数,每次只拿一个个位数最后一定可以将非0的最后一位转化成0

再证明先手必败状态无论如何都会转化成先手必胜状态。

我们知道若想保留先手必败状态就需要拿一个10的倍数,这显然是不可能的,因为所有以0结尾的数字都不是回文数。

至此,证明完成。


代码

#include
using namespace std;
const int N = 100010;
int t;
string s;

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        cin >> s;
        if(s.back() == '0')
            cout << "E\n";
        else
            cout << "B\n";
    }
    return 0;
}

牛奶交换

【蓝桥杯】每日练习 Day4_第4张图片


分析

初看没有什么思路,但是分析一下的话就很容易得出来如果两头奶牛是相对的话——RL,这样他们的牛奶就永远不会减少。

随后再分析LLLLL这样的序列,可以发现这样的情况也是永远不会减少的。

但是其他的牛如何分析呢?

再观察,有的牛是最开始就会减少的比如LRR的中间这头牛,无私奉献却没有半点回报,而右边这头牛是只有在中间的牛没有牛奶后才会减少的——唇亡齿寒啊,所谓敌不动我不动,好像也可以这样来讲。那么如何分析呢?

我先来讲一个小细节,那就是所有的牛奶都是动态的而不是静态的,有的桶会丢弃牛奶的同时灌入牛奶,这让我相当了昨天看到的一个科普知识——虚拟内存。原理很简单,占用内存的同时释放内存,这样内存就会无穷无尽(假装有很大的内存,将所有可输出的牛奶视作虚拟容量)。可以观察到和这道题的过程很像,所以我自然而然的想到了一种策略——虚拟容量,即每个桶的容量都是虚拟的,给每个桶设置一个虚拟容量的属性,然后记录所有进入桶的牛奶数量(最开始的也算),最后减去M就好了。

有了这种策略就可以很方便的推算出最终的结果。但是问题又来了,从哪里开始记录呢?

当时是从起点,即——只出不进的牛,等等,只出不进的牛?这不就是入度为0还有前面那个不会减少的状态好像也是环……再加上每头牛之间你不动我不动的关系……

想到这里,主播恍然大悟,就决定是你了!拓补排序!

结果很明了了,建图,随后拓补排序的过程中记录虚拟容量,随后再通过M推导出容量即可!


代码

/*
    写着写着发现可以抽象成图,然后判环,环内的所有元素必定是无穷的。
    紧接着就有一个问题?拓补排序能不能处理环?
    其实是可以的,因为环上的元素根本不会进拓补排序。
    再加上我前面所说的虚拟容量,结果就很清晰了
*/
#include
#include
using namespace std;
const int N = 200010;
typedef long long LL;
int n, m;
char s[N];
LL a[N], b[N]; //有可能会超范围, 然后存一个蓝本来记录会不会溢出
int p[N]; 
LL l2; //存储总量的
queue que;
int next(int i)
{
    if(s[i] == 'R') return (i + 1) % n;
    return (i - 1 + n) % n;
} //模拟一个环

int main()
{
    cin >> n >> m;
    cin >> s;
    for(int i = 0; i < n; i++)
    {
        cin >> a[i];
        b[i] = a[i];
        int x = next(i); //取出指向位置
        p[x]++; //被指向的位置增加入度值
    }
    
    for(int i = 0; i < n; i++)
    {
        if(p[i] == 0)
            que.push(i); //添加
    }
    
    while(que.size())
    {
        int x = que.front(); que.pop(); //取出来了
        if(--p[next(x)] == 0) //入度为0
        {
            int y = next(x);   
            que.push(y);
            a[y] += a[x]; //记录虚拟容量
        }
    }
    
    for(int i = 0; i < n; i++)
    {
        if(p[i] != 0) //成环了
            l2 += a[i];
        else 
            l2 += min((LL)b[i], max((LL)0, a[i] - m));
    }
    cout << l2;
    return 0;
}

最大限度的提高生产力

【蓝桥杯】每日练习 Day4_第5张图片


分析

看到这道题的时候就有一种熟悉的感觉,之前是不是有一道给定每根芦笋的高度排名和生长趋势,随后将问题转化成不等式求值问题?

观察t + s,对于每次访问若想成功就要满足题目说的小于关门时间,即:t + s < c,而tc都是已知的,可以发现我们可以表示出s,即:

s < c - t,即若想访问到这个节点s必须要小于c - t,我们将这些s存起来,排个序(推荐采用从大到小排序的策略。)

随后对于每次询问若想求能够进行多少次访问只需要求我们存储的s中有多少是大于当前给定时间的就可以了。

而对于线性元素的查找我们是不是可以采用二分或者map?到这里这道题就解出来了,是不是很简单。

来分析一下时间复杂度。

不等式求值O(n),排序O(nlogn),而对于每次询问是O(mlogm)m = 1e6logm约等于2020 * 1e6 = 2e7 < 1e8。所有最终可以通过。


代码

/*
    这是一个离线问题,肯定是要打表的。
    给定t1和s,就是给定访问的时间,转化成n个不等式
    将每次访问的最高限度时间存储起来随后每次询问二分。
    s = 1e6, logs 约等于20, 20 * 1e6 < 1e8,是可以求解的
    t + s < c
    s < c - t;
*/
#include
#include
using namespace std;
const int N = 200010;
int n, q;
int c[N];

int find(int s)
{
    int l = -1, r = n;
    while(l < r)
    {
        int m = (l + r) >> 1;
        if(s >= c[m])
            r = m;
        else
            l = m + 1;
    }
    return l;
} //查找的是闭区间,即小于等于s的所有位置

bool cmp(int x, int y)
{
    return x > y;
}

int main()
{
    cin >> n >> q;
    for(int i = 0; i < n; i++) cin >> c[i];
    for(int i = 0; i < n; i++) {int x; cin >> x; c[i] -= x;}
    sort(c, c + n, cmp); //排序
    //for(int i = 0; i < n; i++)  cout << c[i] << ' ';
    while(q--)
    {
        int s, v;
        cin >> v >> s;
        int x = find(s);
        //cout << x << endl;
        if(x >= v) cout << "YES\n";
        else cout << "NO\n";
    }
    
    
    return 0;
}

你可能感兴趣的:(算法,数据结构,蓝桥杯,c++)