标签:答案 ret queue ring next zoj 例子 highlight 网上
题目描述
输入
输出
第一行包含一个整数K,表示最多能选取的祭祀点的个数。
样例输入
4 4
1 2
3 4
3 2
4 2
样例输出
2
题解
Floyd+拆点+网络流最小割
看了网上那些高端的做法,不明觉厉。
最多能保留几个点,即最少需要去掉几个点,即最小割。
先用Floyd求出两个点是否能同时选择(即不连通),然后建图,如果两个不同的点i和j不能同时选择,则加i->j‘的边,容量为inf。
再加源点s->i的边,容量为1,加i‘->汇点t的边,容量为1。
然后跑dinic即可,答案为n-maxflow。
#include <cstdio> #include <cstring> #include <queue> #define inf 0x7fffffff using namespace std; queue<int> q; int map[110][110] , head[210] , to[12000] , val[12000] , next[12000] , cnt = 1 , dis[210] , s , t; void add(int x , int y , int z) { to[++cnt] = y; val[cnt] = z; next[cnt] = head[x]; head[x] = cnt; } bool bfs() { int x , i; while(!q.empty()) q.pop(); memset(dis , 0 , sizeof(dis)); dis[s] = 1; q.push(s); while(!q.empty()) { x = q.front(); q.pop(); for(i = head[x] ; i ; i = next[i]) { if(val[i] && !dis[to[i]]) { dis[to[i]] = dis[x] + 1; if(to[i] == t) return 1; q.push(to[i]); } } } return 0; } int dinic(int x , int low) { if(x == t) return low; int temp = low , i , k; for(i = head[x] ; i ; i = next[i]) { if(val[i] && dis[to[i]] == dis[x] + 1) { k = dinic(to[i] , min(temp , val[i])); if(!k) dis[to[i]] = 0; val[i] -= k , val[i ^ 1] += k; if(!(temp -= k)) break; } } return low - temp; } int main() { int n , m , i , j , k , x , y , ans = 0; scanf("%d%d" , &n , &m); s = 0 , t = 2 * n + 1; for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x , &y) , map[x][y] = 1; for(k = 1 ; k <= n ; k ++ ) for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= n ; j ++ ) map[i][j] |= map[i][k] & map[k][j]; for(i = 1 ; i <= n ; i ++ ) add(s , i , 1) , add(i , s , 0) , add(i + n , t , 1) , add(t , i + n , 0); for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= n ; j ++ ) if(i != j && map[i][j]) add(i , j + n , inf) , add(j + n , i , 0); while(bfs()) ans += dinic(s , inf); printf("%d\n" , n - ans); return 0; }
标签:答案 ret queue ring next zoj 例子 highlight 网上
原文地址:http://www.cnblogs.com/GXZlegend/p/6476115.html