[BZOJ 1143][CTSC2008]祭祀river

传递闭包

二元关系是一个点选了另一个点就不能选

拆点,求最大独立集

二分图的最大独立集=点数-最大匹配。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define in(u) u
#define out(u) u + n
#define maxn 1000010
using namespace std;

int n, m;

const int inf = 0x7fffffff;

struct Edge{
    int to, next, w;
}edge[maxn << 1];

int h[210], cnt = 1;

void add(int u, int v, int w){
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].next = h[u];
    edge[cnt].w = w;
    h[u] = cnt;
    swap(u, v), w = 0;
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].next = h[u];
    edge[cnt].w = w;
    h[u] = cnt;
}

queue<int>Q;

int d[10010], S, T;

bool BFS(){
    memset(d, -1, sizeof d);
    Q.push(S);d[S] = 0;
    while(!Q.empty()){
        int u = Q.front();Q.pop();
        for(int i = h[u]; i; i = edge[i].next){
            if(!edge[i].w)continue;
            int v = edge[i].to;
            if(d[v] == -1){
                d[v] = d[u] + 1;
                Q.push(v);
            }
        }
    }return d[T] != -1;
}

int DFS(int x, int a){
    if(x == T || a == 0)return a;
    int used = 0, f;
    for(int i = h[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(d[v] == d[x] + 1){
            f = DFS(v, min(a - used, edge[i].w));
            edge[i].w -= f;
            edge[i ^ 1].w += f;
            used += f;
            if(used == a)return used;
        }
    }
    if(used == 0)d[x] = -1;
    return used;
}

int Dinic(){
    int ans = 0;
    while(BFS())
		ans += DFS(S, inf);
    return ans;
}

bool G[210][210];

int main(){
    scanf("%d%d", &n, &m);
    S = 0, T = n * 2 + 1;
    int u, v;
    for(int i = 1; i <= n; i ++){
		add(S, in(i), 1);
		add(out(i), T, 1);
    }
    
	for(int i = 1; i <= m; i ++){
		scanf("%d%d", &u, &v);
		G[u][v] = 1;
	}
	
	for(int k = 1; k <= n; k ++)
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= n; j ++)
				G[i][j] |= G[i][k] & G[k][j];
				
	for(int u = 1; u <= n; u ++)
	    for(int v = 1; v <= n; v ++)
	        if(G[u][v])add(in(u), out(v), 1);
	        
    printf("%d\n", n - Dinic());
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值