[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);
}

 

posted @ 2021-02-02 21:31  hfctf0210  阅读(83)  评论(0编辑  收藏  举报