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;
}