n个布尔变量,满足m个如 A为x或B为y的限制
建一个点拆成两个,分别表示选TRUE或FALSE
建立A的!x B的y 的连边 与 A的x B的!y 的连边
每次dfs。
若一个点在之前条件下无论选TRUE或FALSE都无法满足条件,则无论如何都无法满足条件故无需回溯
——————————————————————————————————————————————
正解tarjan(BZOJ1997)
#include <cstdio> #include <iostream> using namespace std; int pos[10001],x[10001],y[10001],mark[10001],sta[10001],top,nd[10001],next[2000001],des[2000001],nm; int tx[10001],ty[10001],ham[10001],forb[10001][2],cnt,dfn[10001],low[10001],inst[10001],sct; int scc[10001]; void addedge(int x,int y){ next[++cnt]=nd[x];des[cnt]=y;nd[x]=cnt; } int check(int t1,int t2,int t3){ if (t1==t2||t1==t3) return(0); if (t1>t2&&t1<t3) return(1); return(-1); } int inter(int a,int b){ int t1=pos[x[a]],t2=pos[y[a]],t3=pos[x[b]],t4=pos[y[b]]; if (t1>t2){ int t=t1;t1=t2;t2=t; } int ch1=check(t3,t1,t2),ch2=check(t4,t1,t2); return(ch1*ch2); } void tarjan(int po){ dfn[po]=low[po]=++cnt; inst[po]=1;sta[++top]=po; for (int p=nd[po];p!=-1;p=next[p]){ if (!dfn[des[p]]){ tarjan(des[p]);low[po]=min(low[po],low[des[p]]); }else if (inst[des[p]]) low[po]=min(low[po],dfn[des[p]]); } if (low[po]==dfn[po]){ sct++; while (sta[top]!=po){ scc[sta[top]]=sct; inst[sta[top--]]=0; } scc[sta[top]]=sct; inst[sta[top--]]=0; } } int solve(){ for (int i=0;i<2*nm+2;i++) if (!dfn[i]){ top=0; tarjan(i); } for (int i=0;i<2*nm+2;i+=2) if (scc[i]==scc[i^1]) return(0); return(1); } int main(){ int T; scanf("%d",&T); while (T--){ int n,m; scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d",&tx[i],&ty[i]); for (int i=1;i<=n;i++) scanf("%d",&ham[i]); if (m>3*n-6) {printf("NO ");continue;} for (int i=1;i<n;i++) forb[ham[i]][0]=ham[i+1];forb[ham[n]][0]=ham[1]; for (int i=2;i<=n;i++) forb[ham[i]][1]=ham[i-1];forb[ham[1]][1]=ham[n]; for (int i=1;i<=n;i++) pos[ham[i]]=i; nm=-1; for (int i=1;i<=m;i++) if (ty[i]!=forb[tx[i]][0]&&ty[i]!=forb[tx[i]][1]) x[++nm]=tx[i],y[nm]=ty[i]; cnt=0; for (int i=0;i<2*nm+2;i++) nd[i]=-1,dfn[i]=0,low[i]=0,scc[i]=0; for (int i=0;i<=nm;i++) for (int j=i+1;j<=nm;j++) if (inter(i,j)<0){ addedge(2*i,2*j+1); addedge(2*j,2*i+1); addedge(2*i+1,2*j); addedge(2*j+1,2*i); } cnt=0;sct=0; if (solve()) printf("YES ");else printf("NO "); } }
寻找方案时若有方案存在,则无需考虑#include <cstdio>
int n,m,nd[2001],next[8001],des[8001],cnt,mark[2001],sta[2001],top; char ans[2001]; void addedge(int x,int y){ next[++cnt]=nd[x];des[cnt]=y;nd[x]=cnt; } int check(int po){ if (mark[po]) return(1); if (mark[po^1]) return(0); mark[po]=1; sta[++top]=po; for (int p=nd[po];p!=-1;p=next[p]) if (!check(des[p])) return(0); return(1); } int main(){ scanf("%d%d",&n,&m); for (int i=0;i<2*n;i++) nd[i]=-1; for (int i=1;i<=m;i++){ int po1,typ1,po2,typ2;char st[2]; scanf("%d",&po1);po1--;scanf("%s",&st);typ1=(st[0]=='Y'); scanf("%d",&po2);po2--;scanf("%s",&st);typ2=(st[0]=='Y'); addedge(2*po1+!typ1,2*po2+typ2); addedge(2*po2+!typ2,2*po1+typ1); } for (int i=0;i<2*n;i+=2){ top=0;int t1=check(i); for (int j=1;j<=top;j++) mark[sta[j]]=0; top=0;int t2=check(i^1); for (int j=1;j<=top;j++) mark[sta[j]]=0; if (!t1&&!t2) {printf("IMPOSSIBLE ");return(0);} if (t1&&t2) ans[i>>1]='?';else if (t1) ans[i>>1]='N';else ans[i>>1]='Y'; } for (int i=0;i<n;i++) printf("%c",ans[i]); }