• CF1427F-Boring Card Game【贪心】


    正题

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


    题目大意

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

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

    保证有解

    1 ≤ n ≤ 200 1\leq n\leq 200 1n200


    解题思路

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

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

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

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

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

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

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

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

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

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

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

    时间复杂度: O ( n ) O(n) O(n)


    code

    #include
    #include
    #include
    #include
    #include
    #include
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
  • 相关阅读:
    [免费专栏] Android安全之安卓APK浅析
    2.1 OrCAD软件中怎么新建库文件?【OrCAD原理图封装库50问解析】
    怎样做ChatGPT应用开发?
    计算机竞赛 深度学习 opencv python 公式识别(图像识别 机器视觉)
    Hadoop集群的启动顺序
    IOS安全测试学习-DVIA-v2
    Gunicorn部署django报异常 server closed the connection unexpectedly
    主成分分析笔记
    Jmeter介绍以及脚本制作与调试
    maven命令上传文件到私服deploy-file
  • 原文地址:https://blog.csdn.net/Mr_wuyongcong/article/details/126274041