poj3281 - Dining

133 篇文章 0 订阅

                              想看更多的解题报告:http://blog.csdn.net/wangjian8006/article/details/7870410
                                  转载请注明出处:
http://blog.csdn.net/wangjian8006

 

题目大意:有F种食物,D种饮料
N头奶牛,只能吃某种食物和饮料(而且只能吃特定的一份)
一种食物被一头牛吃了之后,其余牛就不能吃了
第一行有N,F,D三个整数
接着2-N+1行代表第i头牛,前面两个整数是Fi与Di(食物与饮料的种类数量),接着是食物的种类与饮料的种类
要求输出最多分配能够满足的牛的数量

解题思路:建图,有2*n+f+d+2个顶点
0表示源点,2*n+f+d+1表示汇点
由源点指向食物,再由食物指向牛,牛再指向对应的饮料,饮料再指向汇点
当然要使每一头牛都对应每一份食物与饮料,所以应该牛i指向牛i再指向饮料,这样就可以避免一头牛只占用多份食物与饮料了
全部是有向的边,而且权值全部为1
我在这里是1到f为食物点,f+1到f+2*n为牛点,f+2*n+1到f+2*n+d为饮料点

 

/*
dinic
Memory 836K
Time   47MS
*/
#include <iostream>
#include <queue>
using namespace std;

#define MAXV 410
#define INF INT_MAX
#define min(a,b) (a>b?b:a)

int res[MAXV][MAXV];		//残量
int	dis[MAXV];				//表示多少层
int source,sink,n,maxflow;	//n为总的点数

int bfs(){
	int k;
	queue<int> q;
    memset(dis,-1,sizeof(dis));
    dis[sink]=0;
	
    q.push(sink);
    while(!q.empty()){
		k=q.front();
		q.pop();
        for(int i=0;i<n;i++){
            if(dis[i]==-1 && res[i][k]){
                dis[i] = dis[k] + 1;
                q.push(i);
            }
        }
        if(k==source) return 1;
    }
	return 0;
}

int dfs(int cur,int cp){
    if(cur==sink)	return cp;
	
    int tmp=cp,t;
    for(int i=0;i<n && tmp;i++){
        if(dis[i]+1==dis[cur] && res[cur][i]){
            t=dfs(i,min(res[cur][i],tmp));
            res[cur][i]-=t;
            res[i][cur]+=t;
            tmp-=t;
        }
    }
    return cp-tmp;
}

void dinic(){
    maxflow=0;
    while(bfs()) maxflow+=dfs(source,INF);
}

int main(){
	int f,d;
	int i,j,f_sum,d_sum,tmp;
	while(~scanf("%d%d%d",&n,&f,&d)){
		memset(res,0,sizeof(res));
		source=0,sink=2*n+f+d+1;

		for(i=1;i<=f;i++) res[source][i]=1;		//源点指向食物
		for(i=1;i<=d;i++) res[2*n+f+i][sink]=1;		//饮料指向汇点
		for(i=1;i<=n;i++) res[f+i][f+n+i]=1;	//牛指向牛
		//点的顺序,食物,奶牛,饮料

		for(i=1;i<=n;i++){
			scanf("%d%d",&f_sum,&d_sum);
			for(j=1;j<=f_sum;j++){
				scanf("%d",&tmp);
				res[tmp][f+i]=1;		//食物指向牛
			}
			for(j=1;j<=d_sum;j++){
				scanf("%d",&tmp);
				res[i+f+n][2*n+f+tmp]=1;	//牛指向饮料
			}
		}
		n=sink+1;				//代表总的点数了
		
		dinic();
		printf("%d\n",maxflow);
	}
	return 0;
}


 

===========================================================

 

 

/*
sap
Memory 828K
Time   32MS
*/
#include<iostream>
using namespace std;
#define MAXV 410
#define INF INT_MAX
#define min(a,b) (a>b?b:a)

int res[MAXV][MAXV],source,sink,n;
int pre[MAXV],dis[MAXV],gap[MAXV],maxflow,cur[MAXV];

int sap(){
	int s=source,t=sink;
	memset(cur,0,sizeof(cur));
	memset(dis,0,sizeof(dis));
	memset(gap,0,sizeof(gap));
	
	int u=pre[s]=s,aug=INF,v;
	maxflow=0;
	gap[source]=n;
	
	while(dis[s]<n){
loop:
	for(v=cur[u];v<n;v++)
		if(res[u][v] && dis[u]==dis[v]+1){
			cur[u]=v;
			aug=min(aug,res[u][v]);
			pre[v]=u;
			u=v;
			if(v==t){
				maxflow+=aug;
				for(u=pre[u];v!=s;v=u,u=pre[u]) res[u][v]-=aug,res[v][u]+=aug;
				aug=INF;
			}
			goto loop;
		}
		
		int mind=n;
		for(v=0;v<n;v++)
			if(res[u][v]&&(mind>dis[v])){
				cur[u]=v;
				mind=dis[v];
			}
			
			if((--gap[dis[u]])==0) break;
			
			gap[dis[u]=mind+1]++;
			u=pre[u];
	}
	return maxflow;
}

int main(){
	int f,d;
	int i,j,f_sum,d_sum,tmp;
	while(~scanf("%d%d%d",&n,&f,&d)){
		memset(res,0,sizeof(res));
		source=0,sink=2*n+f+d+1;

		for(i=1;i<=f;i++) res[source][i]=1;		//源点指向食物
		for(i=1;i<=d;i++) res[2*n+f+i][sink]=1;		//饮料指向汇点
		for(i=1;i<=n;i++) res[f+i][f+n+i]=1;	//牛指向牛
		//点的顺序,食物,奶牛,饮料

		for(i=1;i<=n;i++){
			scanf("%d%d",&f_sum,&d_sum);
			for(j=1;j<=f_sum;j++){
				scanf("%d",&tmp);
				res[tmp][f+i]=1;		//食物指向牛
			}
			for(j=1;j<=d_sum;j++){
				scanf("%d",&tmp);
				res[i+f+n][2*n+f+tmp]=1;	//牛指向饮料
			}
		}
		n=sink+1;				//代表总的点数了

		sap();
		printf("%d\n",maxflow);
	}
	return 0;
}


 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值