poj3281 dining 经典最大流建模方法
2015-08-17 23:58
363 查看
题意:有f中食物和D种饮料,每头牛只能享用一种食物和饮料,每个食物跟饮料也只能被一头牛享用。现在有n头牛,每头牛都有自己喜欢的食物种类列表和饮料列表,问最多能使几头牛同时享用到自己喜欢的食物和饮料。f,d,n都是一百以内的。
思路:就不说一开始的想法了,是最近学习的最大流的建模里面的新的方法。
之前做过几道题,比如poj2391这道,它是比较一般的左边一些点代表着供应,2391这道题就是每个点的牛的数量,右边一个点集代表了需求并与汇点连接,这道题就是每个点能容纳的牛数,然后拆点联一下套模板就好了。
而现在这道题跟2391这道有点相似,那我们就可以把食物当作供应,经过牛这些点,然后走到代表饮料的需求的点。
我个人认为也可以这么理解吧,每一个食物从源点出来,然后经过饮料进入汇点才能组成一对,然后中间必须经过喜欢它们的牛。
但是有一个问题就是如果单纯的食物-》牛-》饮料,那么就会出现多对食物与饮料被一个牛享用的情况,所以把牛拆了,变成食物->牛->牛->饮料,其中这两个牛是同一个,边的容量都是1,这么的话就会避免,一个牛享用多对的情况了,因为牛->牛的容量是1。
至于模板,dinic与sap都行,速度差不多,有些情况下貌似sap稍快些。
代码:
思路:就不说一开始的想法了,是最近学习的最大流的建模里面的新的方法。
之前做过几道题,比如poj2391这道,它是比较一般的左边一些点代表着供应,2391这道题就是每个点的牛的数量,右边一个点集代表了需求并与汇点连接,这道题就是每个点能容纳的牛数,然后拆点联一下套模板就好了。
而现在这道题跟2391这道有点相似,那我们就可以把食物当作供应,经过牛这些点,然后走到代表饮料的需求的点。
我个人认为也可以这么理解吧,每一个食物从源点出来,然后经过饮料进入汇点才能组成一对,然后中间必须经过喜欢它们的牛。
但是有一个问题就是如果单纯的食物-》牛-》饮料,那么就会出现多对食物与饮料被一个牛享用的情况,所以把牛拆了,变成食物->牛->牛->饮料,其中这两个牛是同一个,边的容量都是1,这么的话就会避免,一个牛享用多对的情况了,因为牛->牛的容量是1。
至于模板,dinic与sap都行,速度差不多,有些情况下貌似sap稍快些。
代码:
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<queue> using namespace std; const int maxn=50000; struct node { int u,v,next,c; }; node edge[maxn<<1]; int head[500]; int cnt; int dis[500]; int m,n; int ans; void init() { memset(head,-1,sizeof(head)); cnt=0; memset(dis,-1,sizeof(dis)); ans=0; } void add(int a,int b,int c) { edge[cnt].u=a; edge[cnt].v=b; edge[cnt].c=c; edge[cnt].next=head[a]; head[a]=cnt++; } int bfs()// 给各点分层,离源点的远近分 { memset(dis, -1, sizeof(dis)); queue<int> q; dis[0] = 0; q.push(0); int i; int cur; while(!q.empty()) { cur = q.front(); q.pop(); for(i = head[cur]; i != -1; i = edge[i].next) { if(dis[edge[i].v] == -1 && edge[i].c > 0) { dis[edge[i].v] = dis[cur] + 1; q.push(edge[i].v); } } } if(dis[m] < 0) return 0; return 1; } int Find(int x,int low) //找增广 { int a; if(x==m) return low; for(int i=head[x]; i!=-1; i=edge[i].next) { int v=edge[i].v; if(dis[v]==dis[x]+1 && edge[i].c>0 &&(a=Find(v,min(low,edge[i].c)))) { edge[i].c -=a; edge[i^1].c +=a; return a; } } return 0; } void dinic() { int temp; while(bfs()) { temp=Find(0,0x3f3f3f3f); ans+=temp; } printf("%d\n",ans); } int main() { int f,d; int i,j,f_sum,d_sum,tmp; int source,sink; while(~scanf("%d%d%d",&n,&f,&d)) { init(); source=0,sink=2*n+f+d+1; for(i=1; i<=f; i++) { add(source,i,1); add(i,source,0); } //源点指向食物 for(i=1; i<=d; i++) { add(2*n+f+i,sink,1); add(sink,2*n+f+i,0); } //饮料指向汇点 for(i=1; i<=n; i++) { add(f+i,n+f+i,1); add(n+f+i,f+i,0); } //点的顺序,食物,奶牛,饮料 for(i=1; i<=n; i++) { scanf("%d%d",&f_sum,&d_sum); for(j=1; j<=f_sum; j++) { scanf("%d",&tmp); add(tmp,f+i,1); add(f+i,tmp,0); //食物指向牛 } for(j=1; j<=d_sum; j++) { scanf("%d",&tmp); add(n+i+f,2*n+f+tmp,1); //牛指向饮料 add(2*n+f+tmp,n+i+f,0); } } m=sink; dinic(); } return 0; }
相关文章推荐
- NYOJ 64 鸡兔同笼
- UVALive 6838 Flipping Parentheses // 线段树 区间修改 最值查询
- C/C++获取本地时间常见方法
- 让大家学会复用
- Codeforces Round #249 (Div. 2) (模拟)
- C/C++获取本地时间常见方法
- Centos 6.4 KVM安装和配置
- Android 双进程Service常驻后台,无惧“一键清理
- apache和IIS同时存在,apache建立多个网站
- Codeforces Gym 100431B Binary Search 搜索+组合数学+高精度
- JQuery学习笔记 选择器 序
- XML报错
- javascript
- 《Java解惑》读书笔记
- CSU 1542 Flipping Parentheses(线段树)
- Java设计思想(1)
- Java基础---面向对象
- 手把手实现andriod应用增量升级
- hdu 1166 敌兵布阵
- JavaScript权威指南_156_第16章_脚本化CSS_16.6-脚本化样式表