zoukankan      html  css  js  c++  java
  • LOJ_2305_「NOI2017」游戏 _2-sat

    LOJ_2305_「NOI2017」游戏 _2-sat

    题意:

    给你一个长度为n的字符串S,其中第i个字符为a表示第i个地图只能用B,C两种赛车,为b表示第i个地图只能用A,C两种赛车,为c表示第i个地图只能用A,B两种赛车。

    另有d(d<=8)个字符x,表示这个地图三种车都能用。有m个要求,(i,hi,j,hj)表示如果在第i场用了hi,在第j场必须用hj。

    求一种满足要求的方案,若无解输出-1。

    样例输入

    3 1
    xcc
    1
    1 A 2 B

    样例输出

    ABA


    分析:先思考如果没有万能的x该怎么做。每个地图只有两种选择,不妨把这两种选择看作0和1,然后按要求建图。

    1.如果hi不在i可选的范围内,显然不需要做任何事。

    2.如果hj不在j可选的范围内,显然不能选hi这个,那么我们连i(hi)->i(hi^1)表示i位置不能选hi。

    3.如果i==j,当hi!=hj时仿照2那么连边,否则什么也不做

    其他的情况:连i(hi)->j(hj) 和 j(hj^1)->i(hi^1)

    然后tarjan缩点,如果i(0)和i(1)在一个强连通分量中,则不合法,否则输出方案。

    输出方案时对于点i的两种选择,要选择拓扑序靠前的那个,但其实缩点后的强连通分量的编号是满足拓扑序的(这个真的厉害),直接输出即可。

    然后考虑有x怎么做,首先一个想法是3^d枚举x是什么,然后验证。但其实是不必要的,因为x相当于三个都可能选,如果x这里既不能是a,也不能是b,那么枚举c就是没有意义的。

    因为我们枚举a和b就已经包含了所有情况。所以只需2^d枚举即可。

    代码:

    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <stdlib.h>
    using namespace std;
    #define N 100050
    char s[N],op1[10],op2[10];
    int n,d,m;
    int head[N],to[N<<1],nxt[N<<1],cnt,low[N],dfn[N],tot,scc,fs[N],sd[N];
    int xx[N],yy[N],zz[N],ww[N],pos[10],ins[N],S[N],bl[N];
    inline void add(int u,int v) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    void tarjan(int x) {
    	dfn[x]=low[x]=++tot;
    	int i;
    	S[++S[0]]=x;
    	ins[x]=1;
    	for(i=head[x];i;i=nxt[i]) {
    		if(!dfn[to[i]]) {
    			tarjan(to[i]);
    			low[x]=min(low[x],low[to[i]]);
    		}else if(ins[to[i]]) {
    			low[x]=min(low[x],dfn[to[i]]);
    		}
    	}
    	if(low[x]==dfn[x]) {
    		int t=S[S[0]--]; bl[t]=++scc; ins[t]=0;
    		while(x!=t) {
    			t=S[S[0]--]; bl[t]=scc; ins[t]=0;
    		}
    	}
    }
    void init() {
    	memset(head,0,sizeof(head));
    	memset(dfn,0,sizeof(dfn));
    	cnt=0; tot=0; scc=0; S[0]=0;
    }
    bool check() {
    	int i;
    	init();
    	for(i=1;i<=m;i++) {
    		int u=xx[i],v=zz[i],g=yy[i],h=ww[i];
    		if(g!=fs[u]&&g!=sd[u]) continue;
    		if(u==v) {
    			if(g!=h) {
    				if(g==fs[u]) add(u,u+n);
    				else add(u+n,u);
    			}
    			continue;
    		}
    		if(h==fs[v]||h==sd[v]) {
    			if(g==fs[u]) {
    				if(h==fs[v]) {
    					add(u,v);
    					add(v+n,u+n);
    				}else {
    					add(u,v+n);
    					add(v,u+n);
    				}
    			}else {
    				if(h==fs[v]) {
    					add(u+n,v);
    					add(v+n,u);
    				}else {
    					add(u+n,v+n);
    					add(v,u);
    				}
    			}
    		}else {
    			if(g==fs[u]) add(u,u+n);
    			else add(u+n,u);
    		}
    	}
    	for(i=1;i<=n+n;i++) if(!dfn[i]) tarjan(i);
    	for(i=1;i<=n;i++) if(bl[i]==bl[i+n]) return 0;
    	return 1;
    }
    void print() {
    	int i;
    	for(i=1;i<=n;i++) {
    		if(bl[i]<bl[i+n]) {
    			printf("%c",fs[i]+'A'-1);
    		}else {
    			printf("%c",sd[i]+'A'-1);
    		}
    	}
    }
    int main() {
    	//puts("CA"); return 0;
    	//freopen("tt.in","r",stdin);
    	//freopen("tt.out","w",stdout);
    	scanf("%d%d%s%d",&n,&d,s+1,&m);
    	int mask=(1<<d)-1;
    	int i,j;
    	for(i=1;i<=n;i++) {
    		if(s[i]=='x') pos[++pos[0]]=i;
    		if(s[i]=='a') {
    			fs[i]=2; sd[i]=3;
    		}else if(s[i]=='b') {
    			fs[i]=1; sd[i]=3;
    		}else if(s[i]=='c') {
    			fs[i]=1; sd[i]=2;
    		}
    	}
    	for(i=1;i<=m;i++) {
    		scanf("%d%s%d%s",&xx[i],op1,&zz[i],op2);
    		yy[i]=op1[0]-'A'+1; ww[i]=op2[0]-'A'+1;
    	}
    	for(i=0;i<=mask;i++) {
    		for(j=1;j<=d;j++) {
    			if(i&(1<<(j-1))) {
    				fs[pos[j]]=2; sd[pos[j]]=3;
    			}else {
    				fs[pos[j]]=1; sd[pos[j]]=3;
    			}
    		}
    		if(check()) {
    			print();
    			puts("");
    			return 0;
    		}
    	}
    	puts("-1");
    	return 0;
    }
    
  • 相关阅读:
    架构师之路
    责任链设计模式
    Junit框架分析
    线程详解
    课程总结
    IO流
    Java第四次作业
    Character string
    实训
    实训SI
  • 原文地址:https://www.cnblogs.com/suika/p/8886705.html
Copyright © 2011-2022 走看看