[BZOJ] 1143: [CTSC2008]祭祀river

题意:一张DAG上找出一些点,使得它们不能相互到达,求最大点数。

也就是说,找DAG的最长反链,最长反链=n-最小链覆盖,拆点跑二分图最大匹配即可。

注意先用Floyd传递闭包。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

inline int rd(){
  int ret=0,f=1;char c;
  while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
  while(isdigit(c))ret=ret*10+c-'0',c=getchar();
  return ret*f;
}

const int MAXN = 100005;
const int INF=1<<30;

struct Edge{
  int nxt,to,f;
}e[MAXN<<1];
int ecnt=1,head[MAXN],cur[MAXN];
inline void add(int x,int y,int f){
  e[++ecnt].nxt = head[x];
  e[ecnt].to = y;
  e[ecnt].f = f;
  head[x] = ecnt;
}

int dep[MAXN];
queue<int> Q;
bool bfs(int S,int T){
  memset(dep,0,sizeof(dep));dep[S]=1;
  Q.push(S);
  while(!Q.empty()){
    int top=Q.front();Q.pop();
    for(int i=head[top];i;i=e[i].nxt){
      int v=e[i].to;
      if(dep[v]||!e[i].f)continue;
      dep[v]=dep[top]+1;
      Q.push(v);
    }
  }
  return dep[T];
}
int dfs(int x,int flow,int T){
  if(x==T)return flow;
  int used=0,tmp;
  for(int &i=cur[x];i;i=e[i].nxt){
    int v=e[i].to;
    if(dep[v]!=dep[x]+1)continue;
    tmp=dfs(v,min(flow-used,e[i].f),T);
    e[i].f-=tmp;e[i^1].f+=tmp;used+=tmp;
    if(used==flow)return flow;
  }
  if(!used)dep[x]=-1;
  return used;
}

int dinic(int S,int T){
  int ret=0;
  while(bfs(S,T)){memcpy(cur,head,sizeof(head));ret+=dfs(S,INF,T);}
  return ret;
}

int n,m;

int a[105][105];

int main(){
  n=rd();m=rd();
  int x,y;
  for(int i=1;i<=m;i++){
    x=rd();y=rd();
    a[x][y]=1;
  }
  for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
      for(int j=1;j<=n;j++){
        a[i][j]|=a[i][k]&&a[k][j];
      }
    }
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      if(!a[i][j])continue;
      add(i,j+n,1);add(j+n,i,0);
    }
  }
  int S=n+n+1,T=S+1;
  for(int i=1;i<=n;i++){
    add(S,i,1);add(i,S,0);
    add(i+n,T,1);add(T,i+n,0);
  }
  int ans=n-dinic(S,T);
  cout<<ans;
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/ghostcai/p/9646720.html