题目一眼看去以为是4-sat。。。
题意:给n(n<=3000)个黑方块的坐标,保证黑方块没有公共边。对于每个黑方块选一个角作为结点,使得所选结点满足输入的一个无向图。其中距离为曼哈顿距离。输出是否有解。possible或impossible。
对于每个黑方块,4个角落必须选且仅选一个。一开始2-sat建模是对于一个pnt[i],有4对点pnt[i][0], pnt[i][0]', pnt[i][1], pnt[i][1]', pnt[i][2], pnt[i][2]', pnt[i][3], pnt[i][3]'。但是这样建模只能保证4个角落至多选一个(pnt[i][j]连pnt[i][k]' (k!=j)),保证不了至少选一个。
参考别人代码,发现神做法。类似2-sat的嵌套。。。01染色+2sat
把4个角落分组,左上和右下为第0组,右上和左下为第1组。(因为左上和右下的曼哈顿距离为2,有可能共存;右上和左下同理)。而左上和右上不可能共存,左上和左下同理。(没必要也没有意义把不共存的分为一组。)
通过染色,可以确定每个黑方块只有2种选择(黑方块染为第0组,则只能选左上或右下,且必须选且仅选一个;第1组同理)。到这里就可以看得出,显然的2-sat模型。感觉就像是2*2sat==4sat。
#pragma comment (linker,"/STACK:102400000,102400000") #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <vector> #include <algorithm> #include <cmath> #include <queue> #include <set> #include <map> #include <stack> using namespace std; #define eps 1e-8 #define ll long long #define mxn 2600 #define mxe 26000 #define inf 0x3f3f3f3f #define MP make_pair #define maxn 6060 #define maxm 310000*8 struct Edge{ int v,w,nxt; }e[maxm]; int head[maxn],esz; void init(){esz=0;memset(head,-1,sizeof(head));} void addedge(int u,int v,int w){ e[esz].v=v,e[esz].w=w,e[esz].nxt=head[u]; head[u]=esz++; } struct SAT{ Edge e[maxm]; int head[maxn], em; void init(){em=0;memset(head,-1,sizeof(head));} void add(int u,int v){ e[esz].v=v,e[esz].nxt=head[u]; head[u]=esz++; } int dfn[maxn],low[maxn],id,cnt; bool ins[maxn]; int st[maxn],top; int belong[maxn]; void strong(int x,int fa){ dfn[x]=low[x]=++id; st[top++]=x; ins[x]=true; for(int i=head[x];i!=-1;i=e[i].nxt){ int v = e[i].v; if(!dfn[v]){ strong(v,x); low[x] = min(low[x],low[v]); }else if(ins[v]) low[x] = min(low[x], dfn[v]); } if(dfn[x]==low[x]){ int u=-1;++cnt; while(u!=x){ u = st[--top]; ins[u]=false; belong[u]=cnt; } } } bool tarjan(int n){ id=cnt=top=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(ins,0,sizeof(ins)); for(int i=0;i<n;++i) if(!dfn[i]) strong(i,-1); for(int i=0;i<n;i+=2) if(belong[i]==belong[i^1]) return false; return true; } }sat; int dx[2][2]={{0,1},{0,1}}; int dy[2][2]={{0,1},{1,0}}; struct Pnt{ int x,y; Pnt(){} Pnt(int xx,int yy):x(xx),y(yy){} }pnt[maxn]; Pnt pp[maxn][2]; int dis(Pnt a,Pnt b){return abs(a.x-b.x)+abs(a.y-b.y);} int kind[maxn]; bool color(int u,int sign){ kind[u] = sign; for(int i=head[u];i!=-1;i=e[i].nxt){ int v = e[i].v; int sign2 = (dis(pnt[u],pnt[v])&1)^(e[i].w&1)^sign; if(kind[v]==-1){ if(color(v,sign2)==false) return false; }else if(kind[v]!=sign2) return false; } return true; } bool jud(int u,int sign,int n){ memset(kind,-1,sizeof(kind)); if(color(u,sign)==false) return false; for(int i=1;i<=n;++i){ pp[i][0] = Pnt(pnt[i].x+dx[kind[i]][0], pnt[i].y+dy[kind[i]][0]); pp[i][1] = Pnt(pnt[i].x+dx[kind[i]][1], pnt[i].y+dy[kind[i]][1]); } sat.init(); for(int i=1;i<=n;++i){ if(kind[i]==-1) continue; for(int j=head[i];j!=-1;j=e[j].nxt){ int to = e[j].v; int w = e[j].w; int dis1 = dis(pp[i][0], pp[to][0]); int dis2 = dis(pp[i][0], pp[to][1]); if(dis1!=w || dis2!=w){ if(dis1!=w && dis2!=w){ sat.add(2*i-2,2*i-1); }else if(dis1==w){ sat.add(2*i-2,2*to-2); }else if(dis2==w){ sat.add(2*i-2,2*to-1); } } dis1 = dis(pp[i][1], pp[to][0]); dis2 = dis(pp[i][1], pp[to][1]); if(dis1!=w || dis2!=w){ if(dis1!=w && dis2!=w){ sat.add(2*i-1,2*i-2); }else if(dis1==w){ sat.add(2*i-1,2*to-2); }else if(dis2==w){ sat.add(2*i-1,2*to-1); } } } } bool ans = sat.tarjan(n*2); return ans; } int fa[maxn]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} int main(){ int t,n,m; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d%d",&pnt[i].x,&pnt[i].y); scanf("%d",&m); init(); for(int i=0;i<=n;++i) fa[i]=i; for(int i=0;i<m;++i){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); fa[find(u)] = find(v); } bool yes = true; for(int i=1;i<=n;++i){ if(i==fa[i]){ if(jud(i,0,n)==false && jud(i,1,n)==false){ yes = false; break; } } } if(yes) puts("possible"); else puts("impossible"); } return 0; }
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。