zoukankan      html  css  js  c++  java
  • [NOI2017]游戏(2-SAT+枚举)

    题目

    解法

    2-SAT板子题

    如果没有x这个字符的话,问题变成每个点都有两种选法,有一些限制,显然是个2-SAT问题,设拆出来的两个点分别为(i)(i'),则对于限制(x->y),连边((x,y),(y',x')),跑tarjan即可(2-SAT基操嘛。)

    即使现在有了x这个字符,我们也可以假装一个点仍只有两种选法,枚举这两种选法,并且确保(A,B,C)至少在一种选法中存在就行了;具体来说,对于每个x,枚举(A)(B)中哪一个不能选就足以表示出所有情况了

    一句话就是:枚举将每个x变成a或者b,对每种情况跑最简单的(2-SAT)就行了

    输出方案的话比较(color[i])(color[i+n])即可,具体见输出部分

    时间复杂度(O(n*2^n))

    Code

    //因为spacial judge,最后一行不要输出回车
    #include<bits/stdc++.h>
    #define N 100005
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define Min(x,y) ((x)<(y)?(x):(y))
    using namespace std;
    int n,m,d,ref[N][3],rev[N][3];
    int dfn[N],low[N],c,col,color[N];
    int st[N],top;
    int ban[10];
    char lim[N];
    struct R { int x,dx,y,dy; } r[N];
    struct Edge
    {
    	int next,to;
    }edge[N<<3];int head[N],cnt;
    void add_edge(int from,int to)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	head[from]=cnt;
    }
    //0:A   1:B    2:C
    
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    void in()
    {
    	read(n);read(d);
    	scanf("%s",lim);
    	read(m);
    	for(int i=1;i<=m;++i)
    	{
    		char op[2];
    		read(r[i].x); scanf("%s",op); r[i].dx=op[0]-'A'; 
    		read(r[i].y); scanf("%s",op); r[i].dy=op[0]-'A';
    	}
    }
    void tarjan(int rt)
    {
    	dfn[rt]=low[rt]=++c;
    	st[++top]=rt;
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(!dfn[v])
    		{
    			tarjan(v);
    			low[rt]=Min(low[rt],low[v]);
    		}
    		else if(!color[v]) low[rt]=Min(low[rt],dfn[v]);
    	}
    	if(dfn[rt]==low[rt])
    	{
    		color[rt]=++col;
    		while(st[top]!=rt) color[st[top--]]=col;
    		--top;
    	}
    }
    bool work()
    {
    	memset(head,0,sizeof(head));
    	memset(dfn,0,sizeof(dfn));
    	memset(ref,0,sizeof(ref));
    	memset(rev,0,sizeof(rev));
    	memset(color,0,sizeof(color));
    	cnt=top=c=col=0;
    	int ct=0;
    	for(int i=1;i<=n;++i)
    	{	
    		int no,one,two;
    		if(lim[i-1]=='a') no=0,one=1,two=2;
    		else if(lim[i-1]=='b') no=1,one=0,two=2;
    		else if(lim[i-1]=='c') no=2,one=0,two=1;
    		else
    		{
    			++ct;
    			if(ban[ct]) no=0,one=1,two=2;
    			else no=1,one=0,two=2;
    		}
    		ref[i][no]=0,ref[i][one]=1,ref[i][two]=2;
    		rev[i][1]=one,rev[i][2]=two;
    	}
    	for(int i=1;i<=m;++i)
    	{
    		int x=r[i].x,dx=r[i].dx,y=r[i].y,dy=r[i].dy;
    		if(!ref[x][dx]) continue;//不能用这个车
    		else if(!ref[y][dy]) add_edge(x+(ref[x][dx]-1)*n , x+(ref[x][dx]-1 ? 0:n));//一用就死
    		else
    		{
    			add_edge(x+(ref[x][dx]-1)*n , y+(ref[y][dy]-1)*n);
    			add_edge(y+(ref[y][dy]-1?0:n) , x+(ref[x][dx]-1?0:n));//感觉这样子表示1,2节点好蠢。。。 
    		}
    	}
    	for(int i=1,t=(n<<1);i<=t;++i) if(!dfn[i]) tarjan(i);
    	bool ret=1;
    	for(int i=1;i<=n;++i) if(color[i]==color[i+n]) ret=0;
    	return ret;
    }
    int main()
    {
    	in();
    	bool ok=0;
    	for(int i=0,t=(1<<d);i<t;++i)
    	{
    		for(int j=0;j<d;++j) ban[j+1]=(i>>j&1);
    		if(work())
    		{
    			for(int i=1;i<=n;++i) printf("%c",color[i]<color[i+n] ? rev[i][1]+'A' : rev[i][2]+'A');
    			ok=1;
    			break;
    		}
    	}
    	if(!ok) printf("-1");
    	return 0;
    }
    
  • 相关阅读:
    C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器
    C# Winform实现炫酷的透明动画界面
    造轮子了!NETCore跨平台UI框架,CPF
    用HTML,Vue+element-UI做桌面UI
    造轮子,模仿WPF的UI框架,还没完善。。。
    C# Winform开发以及控件开发的需要注意的,被人问怕了,都是基础常识
    sublime 下面开发
    《快学Scala》
    linux中bin与sbin目录的作用及区别介绍
    hadoop深入研究:(十三)——序列化框架
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11660639.html
Copyright © 2011-2022 走看看