3网络流例题详解18.docx
- 文档编号:26138842
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:41
- 大小:53.93KB
3网络流例题详解18.docx
《3网络流例题详解18.docx》由会员分享,可在线阅读,更多相关《3网络流例题详解18.docx(41页珍藏版)》请在冰豆网上搜索。
3网络流例题详解18
网络流例题详解
最大流sap用法和模板
typedefstructnode{
intv,w;
structnode*nxt,*op;
}NODE;
NODEedg[MM];//保存所有的边
NODE*link[NN];//记录节点所在链表的首节点
inth[NN];//距离标号,记录每个点到汇点的距离,这里的距离指的是层数
intnum[NN];//gap优化,标号为i的顶点个数
intM,N,idx,S,T,n;//S表示源点,T表示汇点,n表示节点个数
voidadd(intu,intv,intc){
idx++;
edg[idx].v=v;
edg[idx].w=c;
edg[idx].nxt=link[u];
edg[idx].op=edg+idx+1;
link[u]=edg+idx;
idx++;
edg[idx].v=u;
edg[idx].w=0;
edg[idx].nxt=link[v];
edg[idx].op=edg+idx-1;
link[v]=edg+idx;
}
intMin(inta,intb){
returna
a:
b;
}
intaug(intu,intflow){
if(u==T)returnflow;
intl=flow;//l表示剩余容量
inttmp=n-1;
for(NODE*p=link[u];p;p=p->nxt){
if(h[u]==h[p->v]+1&&p->w){
intf=aug(p->v,Min(l,p->w));
l-=f;
p->w-=f;
p->op->w+=f;
if(l==0||h[S]==n)returnflow-l;//gap
}
//这里是有剩余容量的可行边
if(p->w>0&&h[p->v] tmp=h[p->v]; } } if(l==flow){//如果没有找到增流,才修改标号,刚开始写错了,也杯具的过了好多题 num[h[u]]--;//gap if(num[h[u]]==0)h[S]=n;//gap,每个点的距离值最多为n-1,这里设为n表示断层了 else{ h[u]=tmp+1; num[h[u]]++;//gap } } returnflow-l; } /*n表示总点的个数,包括源点和汇点*/ voidsap(){ intans=0; memset(h,0,sizeof(h));//h保存的是距离标号(到汇点的) memset(num,0,sizeof(num)); num[0]=n; while(h[S] ans+=aug(S,INF); } printf("%d\n",ans); } pku1149最大流(EK算法) 用EK就能做。 盗用大牛讲解: 题目大意: 有M个猪圈(M≤1000),每个猪圈里初始时有若干头猪。 一开始所有猪圈都是关闭的。 依次来了N个顾客(N≤100),每个顾客分别会打开指定的几个猪圈,从中买若干头猪。 每个顾客分别都有他能够买的数量的上限。 每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。 详解见: 原创博客 这题关键: 建一个超级源点,连上所有的商人,边权置为每个顾客能打开的猪圈里猪的总数,建一个超级汇点,每个顾客都连上这个汇点,权值为每个顾客的购买上限,如果猪圈i先被顾客p1打开,紧接着又被p2打开,加一条从p1到p2的边,边权为INF。 我EK写了两遍: 第一个: 407ms #include #include usingnamespacestd; #defineNN104 #defineINF0x5fffffff intM,N; intc[NN][NN]; intmark[NN]; intpre[NN]; voidCreateGraph() { inti,j,cnt,t,x[1004],flag[1004]; for(i=0;i<=N+1;i++){ for(j=0;j<=N+1;j++){ c[i][j]=0; } } for(i=1;i<=M;i++){ scanf("%d",&x[i]); flag[i]=-1;//标记第i个猪圈刚被哪个商人打开过 } for(i=1;i<=N;i++){ scanf("%d",&cnt); for(j=1;j<=cnt;j++){ scanf("%d",&t); if(flag[t]! =-1){ c[flag[t]][i]=INF; }else{ c[0][i]+=x[t]; } flag[t]=i; } scanf("%d",&t); c[i][N+1]=t; } } intBfs() { intque[NN]; memset(mark,0,sizeof(mark)); mark[0]=1; que[0]=0; intcur,i; intflow=INF; intj,num=1; for(j=0;j cur=que[j]; for(i=0;i<=N+1;i++){ if(c[cur][i]>0&&! mark[i]){ mark[i]=1; if(c[cur][i] flow=c[cur][i]; } pre[i]=cur; if(i==N+1){ returnflow; } que[num++]=i; } } } return-1; } voidEdmonds_Karp() { intmaxFlow=0; intflow,tmp; while((flow=Bfs())! =-1){ maxFlow+=flow; tmp=N+1; while(tmp! =0){ c[pre[tmp]][tmp]-=flow; c[tmp][pre[tmp]]+=flow; tmp=pre[tmp]; } } printf("%d\n",maxFlow); } intmain() { scanf("%d%d",&M,&N); CreateGraph(); Edmonds_Karp(); return0; } 第二个: 16ms #include #include #include usingnamespacestd; #defineNN104 #defineINF0x5fffffff intM,N; intc[NN][NN]; intmark[NN]; intpre[NN]; voidCreateGraph() { inti,j,cnt,t,x[1004],flag[1004]; for(i=0;i<=N+1;i++){ for(j=0;j<=N+1;j++){ c[i][j]=0; } } for(i=1;i<=M;i++){ scanf("%d",&x[i]); flag[i]=-1;//标记第i个猪圈刚被哪个商人打开过 } for(i=1;i<=N;i++){ scanf("%d",&cnt); for(j=1;j<=cnt;j++){ scanf("%d",&t); if(flag[t]! =-1){ c[flag[t]][i]=INF; }else{ c[0][i]+=x[t]; } flag[t]=i; } scanf("%d",&t); c[i][N+1]=t; } } intBfs() { intque[NN]; memset(mark,0,sizeof(mark)); mark[0]=1; que[0]=0; intcur,i; intj,num=1; for(j=0;j cur=que[j]; for(i=0;i<=N+1;i++){ if(c[cur][i]>0&&! mark[i]){ mark[i]=1; pre[i]=cur; if(i==N+1){ return1; } que[num++]=i; } } } return-1; } voidEdmonds_Karp() { intmaxFlow=0; intflow,tmp; while(Bfs()==1){ /*查找最小增流的时候,只查找最短路径上的点*/ flow=INF; tmp=N+1; while(tmp! =0){ if(c[pre[tmp]][tmp] flow=c[pre[tmp]][tmp]; } tmp=pre[tmp]; } maxFlow+=flow; tmp=N+1; while(tmp! =0){ c[pre[tmp]][tmp]-=flow; c[tmp][pre[tmp]]+=flow; tmp=pre[tmp]; } } printf("%d\n",maxFlow); } intmain() { scanf("%d%d",&M,&N); CreateGraph(); Edmonds_Karp(); //system("pause"); return0; } 两个代码的区别在于,在查找最小的流值的增量时候,第一个是在搜到的所有节点中找,第二个是在最短路径上找的,范围比第一个小,增量就增加的快,BFS的次数就减少了。 最大流EKdinicsap pku3281Dining 题目分析: 有N头牛,F种食物,D种饮料,要求给每头牛分配一份食物和一种饮料,每种食物和每种饮料只能供一头牛享用,问最多能满足多少头牛的需要。 建图: 建一超级源点标号为0,连上所有的食物,边权为1,建一超级汇点标号为2*N+F+D+1,连上所有的饮料,边权为1,在构造N个点和N头牛一一对应,保证每头牛只享用一种食物和一种饮料,边权为1,每头牛享用的食物和对应的构造点相连,边权为1,N头牛在和他喜欢的饮料相连,边权为1。 数据量小,用EK写了下: 跑了170多ms #include #include #include #defineINF0xfffffff #defineNN410 intS,T; intc[NN][NN]; intpre[NN]; intbfs() { intque[NN]; intmark[NN]; memset(mark,0,sizeof(mark)); que[0]=S; mark[S]=1; inttail,head,u,i; tail=head=0; while(head<=tail){ u=que[head++]; for(i=0;i<=T;i++){ if(! mark[i]&&c[u][i]>0){ mark[i]=1; que[++tail]=i; pre[i]=u; if(i==T){ return1; } } } } return0; } voidEK() { intminf,t; intmaxFlow=0; while(bfs()){ minf=INF; t=T; while(t! =S){ if(c[pre[t]][t] minf=c[pre[t]][t]; } t=pre[t]; } maxFlow+=minf; t=T; while(t! =S){ c[pre[t]][t]-=minf; c[t][pre[t]]+=minf; t=pre[t]; } } printf("%d\n",maxFlow); } intmain() { inti,j,a,b,d,f,N,F,D; scanf("%d%d%d",&N,&F,&D); S=0; T=2*N+F+D+1; memset(c,0,sizeof(c)); for(i=1;i<=F;i++){ c[0][2*N+i]=1; } for(i=1;i<=D;i++){ c[2*N+F+i][T]=1; } for(i=1;i<=N;i++){ scanf("%d%d",&f,&d); for(j=1;j<=f;j++){ scanf("%d",&a); c[2*N+a][N+i]=1; } c[N+i][i]=1; for(j=1;j<=d;j++){ scanf("%d",&b); c[i][2*N+F+b]=1; } } EK(); //system("pause"); return0; } 昨天刚看dinic,熟悉一下代码,又写了一个dinic的,用的单路增广dfs: 0ms #include #include #include #defineINF0xfffffff #defineMM60500 #defineNN410 intS,T,idx; typedefstructnode{ intv,wt; structnode*nxt; }NODE; NODEedg[MM]; NODE*link[NN]; inth[NN]; intque[NN]; intMin(inta,intb){ returna a: b; } voidadd(intu,intv,intx,inty){ edg[idx].v=v; edg[idx].wt=x; edg[idx].nxt=link[u]; link[u]=edg+idx; idx++; edg[idx].v=u; edg[idx].wt=y; edg[idx].nxt=link[v]; link[v]=edg+idx; idx++; } intbfs() { intu,v,w,tail,head; memset(h,-1,sizeof(h)); que[0]=S; h[S]=0; tail=head=0; while(head<=tail){ u=que[head++]; for(NODE*p=link[u];p;p=p->nxt){ v=p->v; w=p->wt; if(h[v]==-1&&w>0){ h[v]=h[u]+1; que[++tail]=v; } } } returnh[T]! =-1; } intdfs(intu,intflow){ if(u==T){ returnflow; } inttmpf=0; intv,w,f; for(NODE*p=link[u];p;p=p->nxt){ v=p->v; w=p->wt; if(h[v]==h[u]+1&&w&&tmpf p->wt-=f; intt=p-edg; if(t%2==0){ edg[t+1].wt+=f; }else{ edg[t-1].wt+=f; } tmpf+=f; } } if(tmpf==0)h[u]=-1; returntmpf; } voiddinic() { intans=0; while(bfs()){ ans+=dfs(S,INF); } printf("%d\n",ans); } intmain() { inti,j,a,b,d,f,N,F,D; scanf("%d%d%d",&N,&F,&D); S=0; T=2*N+F+D+1; idx=0; for(i=S;i<=T;i++)link[i]=0; for(i=1;i<=F;i++){ add(0,2*N+i,1,0); } for(i=1;i<=D;i++){ add(2*N+F+i,T,1,0); } for(i=1;i<=N;i++){ scanf("%d%d",&f,&d); for(j=1;j<=f;j++){ scanf("%d",&a); add(2*N+a,N+i,1,0); } //刚开始把这个写到里面去了,用EK的时候邻接矩阵存的,没影响, //后来用了dinic,邻接表一存,多加了好多遍,WA了好多次 add(N+i,i,1,0); for(j=1;j<=d;j++){ scanf("%d",&b); add(i,2*N+F+b,1,0); } } dinic(); return0; } 正在学sap,就找了道题练了练,发现sap如果用递归形式写,真是太简单了。 得感谢大牛: zkw poj1459 PowerNetwork TimeLimit: 2000MS MemoryLimit: 32768K TotalSubmissions: 10201 Accepted: 5556 Description Apowernetworkconsistsofnodes(powerstations,consumersanddispatchers)connectedbypowertransportlines.Anodeumaybesuppliedwithanamounts(u)>=0ofpower,mayproduceanamount0<=p(u)<=pmax(u)ofpower,mayconsumeanamount0<=c(u)<=min(s(u),cmax(u))ofpower,andmaydeliveranamountd(u)=s(u)+p(u)-c(u)ofpower.Thefollowingrestrictionsapply: c(u)=0foranypowerstation,p(u)=0foranyconsumer,andp(u)=c(u)=0foranydispatcher.Thereisatmostonepowertransportline(u,v)fromanodeutoanodevinthenet;ittransportsanamount0<=l(u,v)<=lmax(u,v)ofpowerdeliveredbyutov.LetCon=Σuc(u)bethepowerconsumedinthenet.TheproblemistocomputethemaximumvalueofCon. Anexampleisinfigure1.Thelabelx/yofpowerstationushowsthatp(u)=xandpmax(u)=y.Thelabelx/yofconsumerushowsthatc(u)=xandcmax(u)=y.Thelabelx/yofpowertransportline(u,v)showsthatl(u,v)=xandlmax(u,v)=y.ThepowerconsumedisCon=6.NoticethatthereareotherpossiblestatesofthenetworkbutthevalueofConcannotexceed6. Input Thereareseveraldatasetsintheinput.Eachdatasetencodesapowernetwork.Itstartswithfourintegers: 0<=n<=100(nodes),0<=np<=n(p
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 网络 例题 详解 18