BZOJ5412 : circle
若仅保留这$k$个点仍然有环,那么显然无解。
否则设$A$表示这$k$个点的集合,$B$表示剩下的点的集合,因为是竞赛图,每个集合内部的拓扑关系是一条链,方便起见将所有点按照在所在集合的链上的位置进行重标号。
对于$B$中每个点$i$,求出$l_i$表示最小的$j$,满足$B_i\rightarrow A_j$有边,再求出$r_i$表示最大的$j$,满足$A_j\rightarrow B_i$有边。
那么需要保留$B$中尽可能多的点,满足对于任意两个点$B_j,B_i(j\leq i)$都有$r_j<l_i$,不然就会有环,即$l_i>\max(r_j)(j\leq i)$。
考虑DP,设$f[i][j]$表示考虑$B$中前$i$个点,前面选择的点的$r$的最大值为$j$时最多可以选择几个点,暴力转移即可。
时间复杂度$O(n^2)$。
#include<cstdio> const int N=2010,BUF=12000000; char Buf[BUF],*buf=Buf; int n,m,i,j,x,y,l[N],r[N],L,R,a[N],ca,b[N],cb,q[N],h,t,d[N],cnt;bool g[N][N],vip[N]; int f[N][N],ans; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} void toposort(int S){ for(i=1;i<=n;i++)d[i]=0; for(i=1;i<=n;i++)if(vip[i]==S)for(j=1;j<=n;j++)if(vip[j]==S)d[j]+=g[i][j]; for(h=i=1,t=cnt=0;i<=n;i++)if(vip[i]==S){ cnt++; if(!d[i])q[++t]=i; } while(h<=t)for(x=q[h++],i=1;i<=n;i++)if(vip[i]==S&&g[x][i])if(!(--d[i]))q[++t]=i; } inline void up(int&a,int b){a<b?(a=b):0;} inline int max(int a,int b){return a>b?a:b;} int main(){ fread(Buf,1,BUF,stdin);read(n),read(m); for(i=1;i<=n;i++)for(j=1;j<=n;j++)read(x),g[i][j]=x; for(i=1;i<=m;i++)read(x),vip[x]=1; toposort(1); if(t<cnt)return puts("impossible"),0; for(i=1;i<=cnt;i++)a[++ca]=q[i]; toposort(0); for(i=1;i<=cnt;i++)b[++cb]=q[i]; for(i=1;i<=ca;i++)d[a[i]]=i; for(i=1;i<=cb;i++){ x=b[i]; L=ca+1,R=0; for(j=1;j<=n;j++)if(vip[j]){ y=d[j]; if(g[x][j]){ if(y<L)L=y; }else if(y>R)R=y; } for(j=0;j<=ca;j++)f[i][j]=f[i-1][j]; if(L>R)for(j=0;j<L;j++)up(f[i][j>R?j:R],f[i-1][j]+1); } for(j=0;j<=ca;j++)up(ans,f[cb][j]); ans=cb-ans; if(ans>=m)puts("impossible");else printf("%d",ans); return 0; }