跳过正文
  1. Tags/

2-Sat

2015

codeforces 569 E. New Language (2-sat)

·2 分钟
1/************************************************************************* 2 > File Name: code/cf/#315/E.cpp 3 > Author: 111qqz 4 > Email: rkz2013@126.com 5 > Created Time: 2015年08月15日 星期六 13时48分36秒 6 ************************************************************************/ 7#include<iostream> 8#include<iomanip> 9#include<cstdio> 10#include<algorithm> 11#include<cmath> 12#include<cstring> 13#include<string> 14#include<map> 15#include<set> 16#include<queue> 17#include<vector> 18#include<stack> 19#define y0 abc111qqz 20#define y1 hust111qqz 21#define yn hez111qqz 22#define j1 cute111qqz 23#define tm crazy111qqz 24#define lr dying111qqz 25using namespace std; 26#define REP(i, n) for (int i=0;i<int(n);++i) 27typedef long long LL; 28typedef unsigned long long ULL; 29const int inf = 0x7fffffff; 30const int N=5E2+7; 31int flag[N],flag2[N]; 32int f[N][N]; 33int a[N]; 34char s[N]; 35int ans[N]; 36int len,n,m; 37char s1[13],s2[13]; 38int p1,p2,q1,q2,dq1,dq2; 39void add(int p,int q,int flag[]) 40{ 41 int dq = q * n + p;//找到元辅音状态为q,第p的点的下标 42 for (int i=1;i<=2*n;++i) 43 { 44 if (f[dq][i]==0) continue; 45 flag[i]=1;//找到所有由dp出发的边指向的点,表示选了dp点一定要选的点。 46 } 47} 48bool check(int flag[]) 49{ 50 for (int i=1;i<=n;++i) 51 if (flag[i]==1&&flag[i+n]==1) return false; //判断是否存在矛盾 52 //(选了j点后,既要选择某点k的元音,也要选择某点k的辅音) 53 return true; 54} 55bool dfs(int pos,int x) 56{ 57 if (pos>n) return true;//如果能形成一个长度为n的单词,说明这种语言有word 58 bool g[2]; 59 g[0]=g[1]=false; 60 for (int i=x;i<=len;++i)//从当前字母x往后枚举 61 { 62 for (int j=1;j<=2*n;++j) flag2[j]=flag[j];//为了不影响原始数组,复制一个布尔数组出来。 63 add(pos,a[i],flag2);//找到所有选了pos点一定要选的点 64 if (check(flag2)&&(!g[a[i]])) 65 { 66 g[a[i]]=true; 67 for (int j=1;j<=2*n;++j) flag[j]=flag2[j]; 68 ans[pos]=i;//将第pos位置的字母变成i 69 if (dfs(pos+1,1)) return true; 70 else return false;//只要有一位找不到合适的字母形成单词,那么肯定就构不成单词。 71 } 72 } 73 return false; 74} 75int main() 76{ 77 scanf("%s",s); 78 len=strlen(s); //0表示辅音,1表示原因,下同。 79 for (int i=1;i<=len;++i)//len 表示字母表中一共有的字母的个数 80 { 81 if (s[i-1]=='V') 82 { 83 a[i] = 0; 84 } 85 else 86 { 87 a[i] = 1; 88 } 89 } 90 memset(f,0,sizeof(f)); 91 scanf("%d%d",&n,&m); 92 for (int i=1;i<=m;++i)//1..n表示元音的点,n+1..2*n 表示辅音的点 93 { 94 scanf("%d",&p1); 95 scanf("%s",s1); 96 if (s1[0]=='V') q1=0;else q1=1; 97 scanf("%d",&p2); 98 scanf("%s",s2); 99 if (s2[0]=='V') q2=0;else q2=1; 100 dq1=q1*n+p1;dq2=q2*n+p2;//找到这组关系对应的点。 101 f[dq1][dq2]=1;//连一条由dq1指向dq2的边,表示如果选了dp1点,那么一定选dp2点 102 dq1=(1-q2)*n+p2;dq2=(1-q1)*n+p1;//找到逆否命题对应的点 103 // ("如果选1,一定选2"的逆否命题是,"如果不选2,一定不选1") 104 f[dq1][dq2]=1; //在连一条边 105 } 106 for (int i=1;i<=2*n;++i) f[i][i]=1; 107 for (int k=1;k<=2*n;++k)//floyd ,把所有间接相连的边直接相连 108 { 109 for (int i=1;i<=2*n;++i) 110 for (int j=1;j<=2*n;++j) 111 f[i][j]|=f[i][k]&f[k][j]; 112 } 113 scanf("%s",s+1); 114 bool ok=false; 115 for (int i=n;i>=0;--i) //倒着扫,每次只改变最后一位的字母,字母从小往大枚举 //这样就可以保证字典序最小。 116 { 117 memset(flag,0,sizeof(flag)); 118 for (int j=1;j<=i;++j) 119 { 120 add(j,a[s[j]-'a'+1],flag); 121 ans[j]=s[j]-'a'+1; 122 } 123 if (!check(flag)) continue; 124 if (dfs(i+1,s[i+1]-'a'+1+1)) 125 { 126 ok=true; 127 break; 128 } 129 } 130 if (!ok) printf("-1\n"); 131 else 132 { 133 for (int i=1;i<=n;++i) printf("%c",ans[i]+'a'-1); 134 } 135 return 0; 136}