[JSOI2014]学生选课(二分+2-SAT)
乍看上去是一道难题,实际上是一道裸题。
看到这种直接求最大摸不着头脑的题,首先就想到二分。由于每个人只能选择两种课,因此就是个简单的2-SAT问题,将大于二分值的关系视为限制条件即可。
#include<bits/stdc++.h> using namespace std; const int N=1010; struct edge{int v,nxt;}e[N*N* 4]; int n,tot,num,col,top,tp[N],a[N][2],f[N][N],hd[N*3],dfn[N*3],low[N*3],bel[N*3],st[N*3]; void adde(int u,int v){e[++tot]=(edge){v,hd[u]},hd[u]=tot;} void tarjan(int u) { dfn[u]=low[u]=++num,st[++top]=u; for(int i=hd[u];i;i=e[i].nxt) if(!dfn[e[i].v])tarjan(e[i].v),low[u]=min(low[u],low[e[i].v]); else if(!bel[e[i].v])low[u]=min(low[u],dfn[e[i].v]); if(low[u]==dfn[u]) { col++; do bel[st[top]]=col;while(st[top--]!=u); } } int id(int x,int y){return y*(n+1)+x;} bool check(int mid) { tot=num=col=top=0; memset(hd,0,sizeof hd); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(bel,0,sizeof bel); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(f[i][j]>mid) for(int x=0;x<2;x++)for(int y=0;y<2;y++)if(a[i][x]==a[j][y]) adde(id(i,a[i][x]),id(j,a[j][y^1])),adde(id(j,a[j][y]),id(i,a[i][x^1])); for(int i=1;i<=n;i++) for(int k=0;k<3;k++) if(tp[i]!=k&&!dfn[id(i,k)])tarjan(id(i,k)); for(int i=1;i<=n;i++)if(bel[id(i,a[i][0])]==bel[id(i,a[i][1])])return 0; return 1; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&tp[i]); if(tp[i]==0)a[i][0]=1,a[i][1]=2; if(tp[i]==1)a[i][0]=0,a[i][1]=2; if(tp[i]==2)a[i][0]=0,a[i][1]=1; for(int j=1,x;j<n;j++)scanf("%d",&x),f[i][x]=j; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) f[i][j]=max(f[i][j],f[j][i]); int l=1,r=n-1,mid; while(l<r) { mid=l+r>>1; if(check(mid))r=mid;else l=mid+1; } printf("%d",l); }