CF1427F-Boring Card Game【贪心】

1|0正题

题目链接:https://www.luogu.com.cn/problem/CF1427F


1|1题目大意

有一个16n的序列,两个人轮流操作,每次取走连续的三个数字。

现在给出先手取走的数字集合,要求构造方案。

保证有解

1n200


1|2解题思路

我们给先手取的颜色标为0,后手的颜色标为1

我们考虑一下能不能求出哪些牌是在一次中取走的,这个取法很像一个括号匹配,也就是一次取走的东西中不会产生交叉,而如果不会产生交叉,那么我们按照括号匹配的找法去找也是对的。

所以我们可以用一个栈存按顺序存牌,当栈顶三个颜色相同时就弹出这三个,表示这三个是在同一次中取走的。

并且我们还能建立一些依赖关系,形如取走x之前必须y,这些依赖关系能构成一个森林。

现在相当于给出这样一棵森林,每次取走一个叶子,要求颜色是01交错的。

我们找一下这个森林的性质,会发现每个节点的颜色都和父节点的不同,还有01的数量相等。

一种取法是01都随便取,但是1必须留下一个根到最后取,现在我们证明这种取法的正确性:

首先如果用这种取法正确,那么一个有解的状态就是存在一个为1的根并且存在一个当前要取的颜色的叶子。然后我们证明所有有解状态都能转移到有解状态即可。

  • 假设现在要取0,那么此时01数量相同。假设随便一个0后就没有了1的叶子,此时1的数量比01,并且有一个为1的根,因为没有为1的叶子,应该每个1都能找到一个为0的儿子,但是1的数量比0多,所以显然不合法,假设不成立。

  • 假设现在要取1,那么此时取走随便一个不是最后一个根的101数量相同,假设此时没有为0的儿子。我们每个0去找儿子中的一个1,理论上也应该找得到,但是因为有一个1是根,所以至少有一个0找不到这样一个儿子,所以假设不成立。

所以我们的取法就是除了最后一个为1的根以外其他的都随便取。

时间复杂度:O(n)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<stack> using namespace std; const int N=1500; int n,cnt,wrt,v[N],_v[N],T[N],p[N],s[N],nrt[N]; vector<int> ans,G[N],prt[N]; deque<int> q[2]; void dfs(int x){ if(!G[x].size())q[_v[x]].push_back(x); for(int i=0;i<G[x].size();i++){ int y=G[x][i]; dfs(y);T[y]=x; } return; } void rel(int x){ ans.push_back(x); if(!T[x])return;G[T[x]].pop_back(); if(G[T[x]].empty())q[_v[T[x]]].push_front(T[x]); return; } int main() { scanf("%d",&n);n=n*6; for(int i=1;i<=n;i++)v[i]=1; for(int i=1,x;i<=n/2;i++) scanf("%d",&x),v[x]=0; int top=0;stack<int> z; for(int i=1;i<=n;i++){ s[++top]=i; if(top>2&&v[s[top]]==v[s[top-1]]&&v[s[top]]==v[s[top-2]]){ ++cnt;p[s[top-2]]=cnt;_v[cnt]=v[s[top]]; prt[cnt].push_back(s[top-2]); prt[cnt].push_back(s[top-1]); prt[cnt].push_back(s[top]); while(!z.empty()&&z.top()>s[top-2]) G[cnt].push_back(p[z.top()]),nrt[p[z.top()]]=1,z.pop(); z.push(s[top-2]);top-=3; } } int c=0; for(int i=1;i<=cnt;i++) if(!nrt[i])dfs(i),c+=_v[i]; for(int i=1;i<=n/6;i++){ rel(q[0].front());q[0].pop_front(); if(i==n/6){ if(wrt)ans.push_back(wrt); else rel(q[1].front()); } else{ int x=q[1].front();c-=(_v[x]==1&&!nrt[x]); if(!nrt[x]&&_v[x]==1&&!c) {wrt=x;q[1].pop_front();} rel(q[1].front());q[1].pop_front(); } } for(int i=0;i<ans.size();i++,putchar('\n')) for(int j=0;j<3;j++) printf("%d ",prt[ans[i]][j]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16573907.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(85)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2021-08-10 P7737-[NOI2021]庆典【tarjan,虚树】
2021-08-10 P5494-[模板]线段树分裂
2021-08-10 2021“MINIEYE杯”中国大学生算法设计超级联赛(7)部分题解
2021-08-10 jzoj7212-[2022省赛]染色(color)【根号分治】
点击右上角即可分享
微信分享提示