• poj 3281Dining(网络流 拆点)


    题目链接:http://poj.org/problem?id=3281

    题目大意:John养了N只奶牛,他为奶牛准备了F个食物和D个饮料,但是每只奶牛只对其中的一些饮料和食物感兴趣,现在请制定一些方案,使得尽可能多的奶牛吃到自己感兴趣的食物和饮料,求出最多满足奶牛的个数。

    思路:拆点建图跑dinic算法最大流,每只奶牛拆成两个点,例如第Ni个奶牛拆为Ni 和 Ni ’ , Ni和Ni'之间建立一条双向边,正向流量是1,反向为0,最后奶牛拆成两部分,两部分之间对应的奶牛相互有边,F个食物对应的再与第一部分的奶牛相连边,容量为1,第二部分的奶牛再与D个饮料相连边,容量仍然为1,再建立一个源点和F个食物相连成F条容量为1的边,D个饮料再与新建立的汇点相连建立D条容量为1的边,最终从源点到汇点跑一边dinic得到的最大流就是最多满足的奶牛个数了。

    AC代码:

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn = 405;
    const int MAX = 0x3f3f3f3f;
    struct node{
    	vector<int> vex;
    	vector<int> num;
    }g[maxn];
    struct edge{
    	int u,v,c;
    }e[maxn*maxn];
    int d[maxn];
    int sp,tp;
    int edgenum = 0;
    void addedge(int u,int v){
    	e[edgenum].u = u;
    	e[edgenum].v = v;
    	e[edgenum].c = 1;
    	g[u].vex.push_back(v),g[u].num.push_back(edgenum++); 
    	// 建立双向边操作 
    	e[edgenum].u = v;
    	e[edgenum].v = u;
    	e[edgenum].c = 0;
    	g[v].num.push_back(edgenum++),g[v].vex.push_back(u);   
    }
    int bfs(){
    	memset(d,-1,sizeof(d));
    	queue<int> q;
    	q.push(0),d[0] = 0;
    	while(!q.empty() ){
    		int cur = q.front() ;
    		q.pop() ;
    		for(int i = 0;i<g[cur].num.size() ;i++ ){
    			int te = g[cur].num[i];
    			int now = g[cur].vex[i];
    			if(e[te].c >0 && d[now] == -1){
    				d[now] = d[cur] + 1;
    				q.push(now); 
    			}  
    		}
    	}  
    	return d[tp]!=-1;
    }
    int dfs(int a,int b){
    	int r = 0;
    	if(a == tp){
    		return b;
    	}
    	for(int i = 0;i<g[a].num.size()&& r<b;i++){
    		int v = g[a].vex[i],te = g[a].num[i];
    		if(e[te].c >0 && d[v] == d[a] + 1){
    			int t = min(b - r,e[te].c );
    			t = dfs(v,t);
    			r+=t;
    			e[te].c -=t;
    			e[te^1].c +=t;
    		}  
    	}
    	if(!r){
    		d[a] = -2;
    	}
    	return r;
    }
    int dinic(int s,int t){
    	int total = 0,tc;
    	while(bfs()){
    		while(1){
    			tc = dfs(sp,MAX);
    			if(!tc){
    				break;
    			}
    			total+=tc;
    		}
    	}
    	return total;
    }
    int main(){
    	int n,f,d;
    	cin>>n>>f>>d;
    	int N = f + 2*n;//拆点建图,每个奶牛拆成两个点,点集标号从f+1到f+2*n
    	int Nd = N + d;//饮料的标号为 N+1到N + d 
    	sp = 0,tp = Nd + 1;//源点汇点 
    	for(int i = f + 1;i<=f+n;i++){
    		addedge(i,i+n);//拆点的奶牛相连 
    	}
    	for(int i = 1;i<=f;i++){
    		addedge(sp,i);//源点和食物相连 
    	}
    	for(int i = N + 1;i<=Nd;i++){
    		addedge(i,tp);//饮料和汇点相连 
    	}
    	for(int i = 1;i<=n;i++){
    		int Fi,Di;
    		cin>>Fi>>Di;
    		for(int j = 0;j<Fi;j++){
    			int Tf;
    			cin>>Tf;
    			addedge(Tf,i+f);//拆点建图 食物->奶牛1 
    		}
    		for(int j = 0;j<Di;j++){
    			int Td;
    			cin>>Td;
    			addedge(i+f+n,N+Td);//拆点建图 奶牛2->饮料 
    		}
    	}
    	cout<<dinic(sp,tp);
    	return 0; 
    } 
  • 相关阅读:
    冒泡排序&快速排序
    1252. Cells with Odd Values in a Matrix
    位运算小结
    832. Flipping an Image
    1812. Determine Color of a Chessboard Square
    10、属性、构造函数与析构函数
    09、封装与类成员
    07、面向对象简介
    06、C#异常处理
    03、运算符
  • 原文地址:https://www.cnblogs.com/AaronChang/p/12129641.html
Copyright © 2020-2023  润新知