ACM图论行云流水Word文件下载.docx

上传人:b****7 文档编号:22616416 上传时间:2023-02-04 格式:DOCX 页数:194 大小:369.13KB
下载 相关 举报
ACM图论行云流水Word文件下载.docx_第1页
第1页 / 共194页
ACM图论行云流水Word文件下载.docx_第2页
第2页 / 共194页
ACM图论行云流水Word文件下载.docx_第3页
第3页 / 共194页
ACM图论行云流水Word文件下载.docx_第4页
第4页 / 共194页
ACM图论行云流水Word文件下载.docx_第5页
第5页 / 共194页
点击查看更多>>
下载资源
资源描述

ACM图论行云流水Word文件下载.docx

《ACM图论行云流水Word文件下载.docx》由会员分享,可在线阅读,更多相关《ACM图论行云流水Word文件下载.docx(194页珍藏版)》请在冰豆网上搜索。

ACM图论行云流水Word文件下载.docx

2)在层次图上进行dfs修改残余网络。

我们可以在层次图上进行多次增广。

3)重复1,2的过程,直到找不到增广路为止。

#include<

stdio.h>

string.h>

#defineINF999999999

#definemin(x,y)(x)>

(y)?

(y):

(x)

structp

{

intv,t,k,next;

}edge[51000];

intn,m,k,S,T,head[510],tot,h[510];

voidaddedge(inta,intb,intk)

edge[tot].v=b;

edge[tot].k=k;

edge[tot].t=tot+1;

edge[tot].next=head[a];

head[a]=tot++;

edge[tot].v=a;

edge[tot].k=0;

edge[tot].t=tot-1;

edge[tot].next=head[b];

head[b]=tot++;

}

intbfs()

inti,top,tail,cur,pop[510];

top=tail=0;

memset(h,0xff,sizeof(h));

h[S]=0;

pop[top++]=S;

while(tail!

=top)

{

cur=pop[tail++];

for(i=head[cur];

i!

=-1;

i=edge[i].next)

if(edge[i].k>

0&

&

h[edge[i].v]==-1)

h[edge[i].v]=h[cur]+1;

pop[top++]=edge[i].v;

}

returnh[T]!

intdfs(intt,intflow)

inti,tmpf,f;

if(t==T)returnflow;

tmpf=0;

for(i=head[t];

h[edge[i].v]==h[t]+1&

tmpf<

flow&

(f=dfs(edge[i].v,min(edge[i].k,flow-tmpf))))

edge[i].k-=f;

edge[edge[i].t].k+=f;

tmpf+=f;

if(tmpf==0)h[t]=-1;

returntmpf;

intdinic()

intmaxflow=0;

while(bfs())

maxflow+=dfs(S,INF);

returnmaxflow;

 

Sap+gap算法:

Dinic算法要多次计算层次图,增加了复杂度。

是不是可以不多次计算层次图呢?

答案是肯定,这就产生了SAP算法。

SAP计算的是反向图的层次图,这和原图的层次图是作用是一样的,当然其实Dinic也可以计算反向图的层次图。

计算反向图的层次图是便于重新给顶点标号,即重新确定其层次图。

具体做法为,当我们找到一条经过顶点i的增广路径后,对于所有边<

i,j>

,计算出m=min{h[j]},这是我们就可以把i重新标号为h[i]=min+1。

实际上,我们可以首先不需要计算反向图的层次图,而是把所有顶点的层次标为0,这对效率没多大影响。

优化1):

gap优化(cnt[])。

所谓gap优化就是计算出层次图后,层次出现断层,这是可以确定残余网络中不存在增广路径了,算法就可以提前结束。

这个优化看似微小,实际作用确不小。

做法就是保存某一个标号在残余网络中出现的次数,如果是0,就断层了。

优化2):

当前弧优化(cur[]、cur1[])。

为了使每次找增广路的时间变成均摊O(V),还有一个重要的优化是对于每个点保存“当前弧”:

初始时当前弧是邻接表的第一条弧;

在邻接表中查找时从当前弧开始查找,找到了一条允许弧,就把这条弧设为当前弧;

改变距离标号时,把当前弧重新设为邻接表的第一条弧。

queue>

#defineINF99999999

usingnamespacestd;

inttot,n,m,S,T,h[410],head[510],cnt[510];

inti,tmpf,f,tmp;

tmpf=flow;

tmp=T;

if(h[t]==h[edge[i].v]+1&

edge[i].k>

0)

f=dfs(edge[i].v,min(edge[i].k,tmpf));

tmpf-=f;

if(!

tmpf||h[S]==T+1)returnflow-tmpf;

if(h[edge[i].v]<

tmp&

0)tmp=h[edge[i].v];

(--cnt[h[t]]))h[S]=T+1;

elsecnt[h[t]=tmp+1]++;

returnflow-tmpf;

intsap_gap()

intmaxflow;

maxflow=0;

memset(h,0,sizeof(h));

memset(cnt,0,sizeof(cnt));

while(h[S]<

T+1)

用最大流解路径问题

Poj2455Secret 

Milking 

Machinetime497

题意:

求从S到T的t条路径,且路径上的边都不同,且最大的边最小。

题解:

这类题,要的不是整体的最小费用,一般都是二分答案(最长所允许的边的费用),筛掉二分时mid之上费用的边,然后用最大流算看它是不是满流的来决定继续怎么分。

至于那个正反边都建的问题,其实它是不可能正反都走的,因为我们的目的是要让路径走的最长的那条尽可能短。

如果都走的话,看下面的图

A

/\

/\

B----C

\/

D

假设我们先走A->

B->

C->

D,再走A->

D。

那么实际上,我可以直接走A->

D和A->

C这一段是完全剩下的。

也就是说,对于任何一条被走正反两次的路,你正走的那个起点和反走的那个终点其实是同一个点,先前走的那条路的前一半A->

B,必然和之后走的路的后一半B->

D相连,对于另一边也是这样。

完全可以不走那段被重复走的。

而且如果走了中间那段,反而是多走的。

至于用单源最短路先定下边的方向,无法保证正确,仔细想想就很容易举出反例了。

代码:

intmain()

intk,i,j,a,b,l,t,r,mid,ans;

while(scanf("

%d%d%d"

&

n,&

m,&

k)!

=EOF)

l=INF,r=-1;

for(i=0;

i<

m;

i++)

scanf("

a,&

b,&

t);

if(t>

r)r=t;

if(t<

l)l=t;

line[i].a=a;

line[i].b=b;

line[i].k=t;

l--,r++;

S=n+1,T=n+2;

while(l+1!

=r)

mid=(l+r)/2;

tot=0;

memset(head,0xff,sizeof(head));

if(line[i].k<

=mid)

addedge(line[i].a,line[i].b,1);

addedge(line[i].b,line[i].a,1);

addedge(S,1,k);

addedge(n,T,k);

if(dinic()==k)ans=mid,r=mid;

elsel=mid;

printf("

%d\n"

ans);

return0;

最长递增子序列问题

题意:

给定正整数序列x1…xn.。

1.计算其最长递增子序列的长度s。

2.计算从给定的序列中最多可取出多少个长度为s的递增子序列。

3.如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列。

首先动态规划求出dp[i],表示以第i位为结尾的最长上升序列的长度,求出最长上升序列长度K。

然后把序列每位i拆成两个点<

i.a>

和<

i.b>

,从<

到<

连接一条容量为1的有向边。

建立附加源S和汇T,如果序列第i位有dp[i]=K,从<

到T连接一条容量为1的有向边。

如果dp[i]=0,从S到<

如果j>

i且d[i]<

d[j]且dp[j]=dp[i]+1,从<

j.a>

求网络最大流,就是第二问的结果。

把边(<

1.a>

<

1.b>

)(<

N.a>

N.b>

)(S,<

T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。

inti,j,k,max,d[1000],dp[1000];

%d"

n)!

if(n==0)continue;

for(i=1;

=n;

d[i]);

memset(dp,0,sizeof(dp));

max=0;

for(j=1;

j<

i;

j++)

if(d[j]<

d[i]&

dp[j]+1>

max)

max=dp[j]+1;

dp[i]=max;

if(max<

dp[i])

max=dp[i];

max+1);

S=0;

T=2*n+1;

addedge(i,i+n,1);

if(dp[i]==0)addedge(S,i,1);

if(dp[i]==max)addedge(i+n,T,1);

for(j=i+1;

if(dp[j]==dp[i]+1&

d[j]>

d[i])

addedge(i+n,j,1);

dinic());

if(i==1||i==n)addedge(i,i+n,INF);

elseaddedge(i,i+n,1);

if(dp[i]==0&

i==1)addedge(S,i,INF);

elseif(dp[i]==0)addedge(S,i,1);

if(dp[i]==max&

i==n)addedge(i+n,T,INF);

elseif(dp[i]==max)addedge(i+n,T,1);

Zoj2760HowManyShortestPathtime80ms

题目大意:

求一个有向图中点到点的边不相交的最短路径条数。

算法:

floyd+最大流

思路:

先求出最短路径的大小,然后再每条边依次判断是否在最短路径上,如果在最短路径上则添加一条流量大小为1的边。

最后再求最大流,最大流值就是路径的条数。

判断边是否在最短路径上:

(其中map[i][j]是点i到点j的最短距离,dt[i][j]是边i到j的大小)

n;

for(j=0;

if(i!

=j&

dt[i][j]!

=-1&

map[S][i]!

map[j][T]!

map[S][T]==map[S][i]+dt[i][j]+map[j][T])

addedge(i,j,1);

//说明在最短路径上,则添边

inti,j,k,map[110][110],dt[110][110];

map[i][j]);

dt[i][j]=map[i][j];

dt[i][i]=map[i][i]=0;

%d%d"

S,&

T);

if(S==T)

inf\n"

);

continue;

for(k=0;

k<

k++)

if(k!

=i)for(j=0;

if(j!

=i&

j!

=k)

if(map[i][k]!

map[k][j]!

(map[i][j]==-1||map[i][j]>

map[i][k]+map[k][j]))

map[i][j]=map[i][k]+map[k][j];

if(map[S][T]==-1)

0\n"

Sgu185Twoshortest

求1->

n的两条不相交的最短路(两条路径可以共顶点但是不能共边)

做法:

用spfa求出点1到各个点的最短路径,然后再if(dis[j]==dis[i]+map[i][j])来判断边map[i][j]是否是在最短路径上,如果是就加到图里,结果就得到以1为顶点的最短路径树,1到树中任意一点的连线都是最短路径,首先把这些边加到网络流的边集中,容量为1。

跑一遍1到n的最大流,如果流量>

=2则有解,再从原点深搜路径即可。

确切的来说,只要在后来建的图中随便找一条路径均是原点到该点的最短路(注意边是单向的),又因为限制了流量是1,所以一条边只能选取一次,这样跑出来的流量就一定是不相交最短路的条数。

当然如果在后面建的最短路径树中直接求两条从1到n的路径是不行的,如下图:

如果边的长度都为1,当找的第一条路径是1->

4->

3->

6时,然后就找不到其他最短路径了,也即只能找到一条,但是图中明显有两条最短路径1->

2->

6和1->

5->

6所以直接求两条从1到n的路径是不行的,因此只能用最大流来做。

代码如下:

intspfa()

inti,j,cur,tmp,mark[410],dis[410];

queue<

int>

que;

memset(mark,0,sizeof(mark));

dis[i]=INF;

dis[1]=0;

mark[1]=1;

que.push

(1);

while(!

que.empty())

cur=que.front();

que.pop();

mark[cur]=0;

if(dis[edge[i].v]>

dis[cur]+edge[i].k)

dis[edge[i].v]=dis[cur]+edge[i].k;

if(mark[edge[i].v]==0)

mark[edge[i].v]=1;

que.push(edge[i].v);

if(dis[n]==INF)return0;

tmp=tot;

S=0,T=n+1;

map[i][j]!

=-1)

if(dis[i]==dis[j]+map[j][i])

addedge(j,i,1);

if(dis[j]==dis[i]+map[i][j])

addedge(S,1,2);

addedge(n,T,2);

return1;

voiddfs2(intt)

inti;

%d"

t);

if(t==n)return;

if(i%2==0&

edge[i].k==0)

dfs2(edge[i].v);

edge[i].k=1;

return;

inti,j,a,b,c;

m);

memset(map,0xff,sizeof(map));

c);

if(map[a][b]==-1||map[a][b]>

c)

map[a][b]=map[b][a]=c;

if(map[i][j]!

addedge1(i,j,map[i][j]);

if(spfa()==0||sap_gap()<

2)

Nosolution\n"

else

dfs2

(1);

\n"

SPOJ962IntergalacticMap

题目大意:

在一个无向图中,一个人要从A点赶往B点,之后再赶往C点,且要求中途不能多次经过同一个点。

问是否存在这样的路线。

(3<

=N<

=30011,1<

=M<

=50011)

建模方法:

由于每个点只能走一次,似乎最短路之类的算法不能用,只有往网络流上靠。

将每个点i拆成两个点i’,i’’并加边(i’,i’’,1)就能轻易达到这个目的。

然后我们以B为源点,A、C为汇点,看能否增广两次。

inti,ca,a,b;

freopen("

1.txt"

"

r"

stdin);

ca);

while(ca--)

b);

addedge(a,b,1);

addedge(b,a,1);

S=n+1;

T=n+2;

addedge(S,2,2);

addedge(1,T,1);

addedge(3,T,1);

if(dinic()==2)printf("

YES\n"

elseprintf("

NO\n"

return

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 畜牧兽医

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1