P7078 [CSP-S2020] 贪吃蛇 题解

P7078 [CSP-S2020] 贪吃蛇

这题好啊

题目传送门

看到题之后觉得有点像砍蚯蚓的那道题

看看题目

可以证明,若一条蛇在吃完之后不是最弱的那一条蛇,那么他一定会选择吃,证明如下

设蛇长为 \(a_{1, \dots ,n}\) 且依次递增,那么很明显的

因为

​ $$a_x>a_y>a_n>a_m$$
​ $$ a_x-a_m>a_y-a_n$$
也就是说,这条蛇吃掉后面的蛇之后产生的蛇永远都不会是最小的哪一个

而如果一条蛇在吃完了之后是最弱的蛇,那么他为了保命,会看自己吃了之后会不会被吃,也就是看下一条蛇的决策

而下一条蛇的决策过程和这一条蛇也是一样的,这种情况就可以 递归的来判断.

还要补充两点

  • 如何维护最强最弱的蛇?

可以用平衡树,set 等,但这种做法会带上一只 \(log\) ,所以一般会T

正确的做法是像砍蚯蚓的那道题一样,用双端队列维护

像这道题,用两个双端队列,一个维护所有原来的蛇,另一个维护吃完蛇后产生的蛇.

对于第二个双端队列的单调性的证明,就是我们一开始证的东西, 后吃的蛇所产生的蛇的长度一定 比 先吃的产生的长度小.

  • 处理一次第二种情况后就可以立即停止了

这是因为第二种情况若吃了,就说明下一条蛇不会吃,结束

若没吃,也结束

上代码!

点击查看代码

#include <bits/stdc++.h>
using namespace std;
struct snack
{
    int len, num;
    //重载了一下运算符
    friend bool operator<(const snack &a, const snack &b)
    {
        if (a.len == b.len)
            return a.num < b.num;
        return a.len < b.len;
    }
    friend bool operator>(const snack &a, const snack &b)
    {
        if (a.len == b.len)
            return a.num > b.num;
        return a.len > b.len;
    }
    friend snack operator-(const snack &a, const snack &b)
    {
        snack tmp;
        tmp.len = a.len - b.len;
        tmp.num = a.num;
        return tmp;
    }

    void clear()
    {
        this->len = 0;
        this->num = 0;
    }
};
snack snac[10000100]; //蛇群们
int t, n, k;
deque<snack> q1, q2; //两个双端队列来维护
void clean_up()
{
    q1.clear();
    q2.clear();
}
snack get_min(bool del) // del决定是否从队列中删除这条蛇
{
    snack ll;
    if (q1.empty() && q2.empty())
    {
        snack insid;
        insid.len = -1;
        insid.num = -1;
        return insid;
    }
    else if (q1.empty())
    {
        ll = q2.front();
        if (del)
            q2.pop_front();
        return ll;
    }
    else if (q2.empty())
    {
        ll = q1.front();
        if (del)
            q1.pop_front();
        return ll;
    }
    else
    {
        if (q1.front() < q2.front())
        {
            ll = q1.front();
            if (del)
                q1.pop_front();
            return ll;
        }
        else
        {
            ll = q2.front();
            if (del)
                q2.pop_front();
            return ll;
        }
    }
}
snack get_max(bool del) //取最大蛇,del定义同上
{
    snack ll;
    if (q1.empty() && q2.empty())
    {
        snack insid;
        insid.len = -1;
        insid.num = -1;
        return insid;
    }
    else if (q1.empty())
    {
        ll = q2.back();
        if (del)
            q2.pop_back();
        return ll;
    }
    else if (q2.empty())
    {
        ll = q1.back();
        if (del)
            q1.pop_back();
        return ll;
    }
    else
    {
        if (q1.back() > q2.back())
        {
            ll = q1.back();
            if (del)
                q1.pop_back();
            return ll;
        }
        else
        {
            ll = q2.back();
            if (del)
                q2.pop_back();
            return ll;
        }
    }
}
int lef = n, llef;
bool fight() //看是否会吃
{
    snack min_, max_;
    if (llef == 2) //如果只剩两条蛇了,一定吃
    {
        return 1;
    }
    min_ = get_min(1);
    max_ = get_max(1);
    if (max_ - min_ > get_min(0)) //如果吃完了不是最小的蛇
    {
        return 1;
    }
    else //吃完是最小的蛇
    {
        llef--;
        q2.push_front(max_ - min_);
        return !fight(); //看下一条会不会吃,会吃则不吃,不吃则吃
    }
}
int solve()
{
    for (int ww = 1; ww <= n; ww++)
    {
        q1.push_back(snac[ww]); //将蛇放入队列
    }
    // case1;
    lef = n;
    snack new_, tmp;
    while (1)
    {
        if (lef == 2)
        {
            lef--;
        }
        if (lef == 1)
        {
            break;
        }
        new_ = get_min(1);
        tmp = get_max(1);
        // cout<<"get "<<new_.len<<" "<<tmp.len<<endl;
        //   cout<<(tmp-new_).len<<" 对 " <<get_min(0).len<<endl;
        if (tmp - new_ > get_min(0)) //如果吃完不是最小的,一定吃
        {
            q2.push_front(tmp - new_);
            lef--;
        }
        else //是最小的
        {
            q2.push_front(tmp - new_);
            break; //结束
        }
    }
    if (lef == 1) //如果只剩一条蛇了,结束
    {
        return lef;
    }
    // case2,判断完直接就结束了
    llef = lef;
    if (!fight()) //看下一条会不会吃
    {
        lef--;
    }
    return lef;
}
int main()
{
    // freopen("P7078_5.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin >> t;
    cin >> n;
    for (int yy = 1; yy <= n; yy++)
    {
        cin >> snac[yy].len;
        snac[yy].num = yy;
    }
    cout << solve() << "\n";
    for (int ww = 2; ww <= t; ww++)
    {
        clean_up();
        cin >> k;
        int a, b;
        for (int ww = 1; ww <= k; ww++)
        {
            cin >> a >> b;
            snac[a].len = b;
        }
        cout << solve() << "\n";
    }

    return 0;
}

posted @ 2024-10-06 15:13  sea-and-sky  阅读(22)  评论(0编辑  收藏  举报