图论算法.docx
- 文档编号:7660841
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:18
- 大小:19.61KB
图论算法.docx
《图论算法.docx》由会员分享,可在线阅读,更多相关《图论算法.docx(18页珍藏版)》请在冰豆网上搜索。
图论算法
1.Dijkstra
1)适用条件&范围:
a)单源最短路径(从源点s到其它所有顶点v);
b)有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图)
c)所有边权非负(任取(i,j)∈E都有Wij≥0);
constintmaxn=160;
#defineIFN9999999
intmap[maxn][maxn];
intdis[maxn];
voiddijkstra(ints,intn)
{
inti,j,k;
intmin;
boolp[maxn];
for(i=1;i<=n;i++)
{
p[i]=false;
dis[i]=map[s][i];
}
dis[s]=0;
p[s]=true;
for(i=1;i<=n;i++)
{
min=IFN;
k=0;
for(j=1;j<=n;j++)
{
if(!
p[j]&&dis[j] { min=dis[j]; k=j; } } p[k]=true; for(j=1;j<=n;j++) {if(! p[j]&&map[k][j]! =IFN&&dis[j]>dis[k]+map[k][j]) { dis[j]=dis[k]+map[k][j]; } } }} intmap[maxn][maxn]; intc[maxn][maxn]; intdis[maxn]; intcost[maxn]; intn,m; voiddijkstra(ints,intend) { inti,j,k; intmin; boolp[maxn]; for(i=1;i<=n;i++) { p[i]=false; dis[i]=map[s][i]; cost[i]=c[s][i]; } dis[s]=0; cost[s]=0; p[s]=true; for(i=1;i<=n;i++) { min=IFN; k=0; for(j=1;j<=n;j++) { if(! p[j]&&dis[j] { min=dis[j]; k=j; } } p[k]=true; for(j=1;j<=n;j++) { if(! p[j]&&map[k][j]! =IFN&&dis[j]>dis[k]+map[k][j]) { dis[j]=dis[k]+map[k][j]; cost[j]=cost[k]+c[k][j]; } elseif(! p[j]&&map[k][j]! =IFN&&dis[j]==dis[k]+map[k][j]&&cost[j]>cost[k]+c[k][j]) { cost[j]=cost[k]+c[k][j]; } } } printf("%d%d\n",dis[end],cost[end]); 2.Floyd-Warshall 1)适用范围: a)APSP(AllPairsShortestPaths) b)稠密图效果最佳 c)边权可正可负 intFloyd() { inti,j,k; for(i=1;i<=n;i++) for(j=1;j<=n;j++) for(k=1;k<=n;k++) if(p[j][k] intflag=1; for(i=1;i<=n;i++) if(p[i][i]>1.0){flag=0;return1;} if(flag)return0; }} 最大流算法: Dinic算法: #defineINF0x1f1f1f1f #defineMIN(a,b)((a)<(b)? (a): (b)) #defineMAX(a,b)((a)>(b)? (a): (b)) #defineN500 intcap[N][N];//容量 intflow[N][N];//流量 intlev[N];//层次 boolvis[N];//标记 intque[100000];//队列 //BSF找层次网络,一次寻找多条增广路径 //st最小顶点标号,ed最大顶点标号,src源点标号,tar汇点标号 boolbfs(intst,inted,intsrc,inttar)//st最小点ed最大点src源点tar汇点 { intfront;//队首 intrear;//队尾 front=rear=0; que[front++]=src; lev[src]=0; memset(vis,0,sizeof(vis)); vis[src]=1; while(rear { intt=que[rear]; rear++; for(inti=st;i<=ed;i++) { if(! vis[i]&&cap[t][i]>flow[t][i]) { vis[i]=1; lev[i]=lev[t]+1; que[front++]=i; } } } returnlev[tar] } //利用层次网络进行增广,每次DFS寻找的是从该节点出发进行DFS增加的总流量 //mn表示从源点至该节点可增广流量 intdfs(intv,intst,inted,inttar,intfl)//fl表示源点到当前顶点的流量 { intret=0; if(v==tar||fl==0)returnfl; for(inti=st;i<=ed;i++) { if(fl==0)break; if(cap[v][i]>flow[v][i]&&lev[v]+1==lev[i]) { intf=MIN(fl,cap[v][i]-flow[v][i]);//沿i点向下可用最大流量 inttt=dfs(i,st,ed,tar,f);//沿i点向下实际增广的流量 if(tt<=0)continue; ret+=tt; fl-=tt;//每次修改fl flow[v][i]+=tt; flow[i][v]-=tt; } } returnret; } intdinic(intst,inted,intsrc,inttar) { intret=0; while(bfs(st,ed,src,tar))//存在可增广路 { intr=dfs(src,st,ed,tar,INF); if(r==0)break; ret+=r; } returnret; } -49- 3.11最小费用最大流编写: 程宪庆校核: 周洲 3.11.1基本原理 最小费用最大流问题是在普通的最大流问题上加了另一个条件,即要每条边单位流量所需的费用,要在求得最大流的情况下所需费用最小。 求最小费用可以转化成求解一个最短路问题,而求最大流需要广度搜索,所以可以使用SPFA最短路算法和EK最大流算法结合的方法求解最小费用最大流问题。 3.11.2模板代码 #defineINF0x3f3f3f3f #defineMIN(a,b)((a)<(b)? (a): (b)) intmat[55][55]; intn,k; inthead[5005]; structArc { intnext_arc; intpoint; intadj; intcost; intcap; }; structArcarc[25000]; intpre[5005]; intdis[5005]; boolfl[5005]; intmax_flow; intmin_cost; intedge_cnt; voidadd(intu,intv,intcst,intcp) { arc[edge_cnt].next_arc=head[u]; arc[edge_cnt].point=v; arc[edge_cnt].adj=u; arc[edge_cnt].cost=cst; arc[edge_cnt].cap=cp; head[u]=edge_cnt; } voidcost_flow(intsrc,inttar) { while (1) { memset(pre,-1,sizeof(pre)); memset(dis,0x3f,sizeof(dis)); memset(fl,0,sizeof(fl)); queue q.push(src); dis[src]=0; while(! q.empty()) { intu=q.front(); q.pop(); fl[u]=0; for(inte=head[u];e! =-1;e=arc[e].next_arc) { if(arc[e].cap>0&&dis[u]+arc[e].cost { dis[arc[e].point]=dis[u]+arc[e].cost; pre[arc[e].point]=e; if(! fl[arc[e].point]) { fl[arc[e].point]=1; q.push(arc[e].point); } } } } if(pre[tar]==-1)break; intmin=INF; for(inti=tar;pre[i]! =-1;i=arc[pre[i]].adj) { min=MIN(min,arc[pre[i]].cap); } for(inti=tar;pre[i]! =-1;i=arc[pre[i]].adj) { arc[pre[i]].cap-=min; arc[pre[i]^1].cap+=min; } max_flow+=min; min_cost+=min*dis[tar]; } } 2.树的最小支配集,最小点覆盖与最大独立集 深度优先遍历,得到深度优先遍历序列。 intp[maxn]; boolselect[maxn]; intnewpos[maxn]; intnow; intn,m; voidDFS(intx) { newpos[now++]=x; intk; for(k=head[x];k! =-1;k=edge[k].next) { if(! select[edge[k].to]) { select[edge[k].to]=true; p[edge[k].to]=x; DFS(edge[k].to); } } } 对于最小支配集,贪心函数如下: intgreedy() { bools[maxn]={0}; boolset[maxn]={0}; intans=0; inti; for(i=n-1;i>=0;i--) { intt=newpos[i]; if(! s[t]) { if(! set[p[t]]) { set[p[t]]=true; ans++; } s[t]=true; s[p[t]]=true; s[p[p[t]]]=true; } } returnans; } 对于最小点覆盖,贪心函数如下: intgreedy() { bools[maxn]={0}; boolset[maxn]={0}; intans=0; inti; for(i=n-1;i>=1;i--) { intt=newpos[i]; if(! s[t]&&! s[p[t]]) { set[p[t]]=true; ans++; s[t]=true; s[p[t]]=true; } } returnans; } 对于最大独立集,贪心函数如下: intgreedy() { bools[maxn]={0}; boolset[maxn]={0}; intans=0; inti; for(i=n-1;i>=0;i--) { intt=newpos[i]; if(! s[t]) { set[t]=true; ans++; s[t]=true; s[p[t]]=true; } } returnans; } 使用样例: intmain() { /*读入图信息*/ memset(select,0,sizeof(select)); now=0; select[1]=true; p[1]=1; DFS (1); printf("%d\n",greedy()); } 该方法经过一次深度优先遍历和一次贪心得到最终解,第一步的时间复杂度是 O(m),由于这是一棵树,m=n-1。 第二步是O(n),一共是O(n)。 在下面的代码中,u表示当前正在处理的节点,p表示u节点的父节点。 对于最小支配集,动态规函数如下: voidDP(intu,intp) { dp[u][2]=0; dp[u][0]=1; bools=false; intsum=0,inc=INF; intk; for(k=head[u];k! =-1;k=edge[k].next) { intto=edge[k].to; if(to==p) continue; DP(to,u); dp[u][0]+=min(dp[to][0],min(dp[0][1],dp[to][2])); if(dp[to][0]<=dp[to][1]) { sum+=dp[to][0]; s=true; } else { sum+=dp[to][1]; inc=min(inc,dp[to][0]-dp[to][1]); } if(dp[to][1]! =INF&&dp[u][2]! =INF) dp[u][2]+=dp[to][1]; elsedp[u][2]=INF; } if(inc==INF&&! s) dp[u][1]=INF; else { dp[u][1]=sum; if(! s) dp[u][1]+=inc; } } 对于最小点覆盖,动态规划函数如下: voidDP() { dp[u][0]=1; dp[u][1]=0; intk,to; for(k=head[u];k! =-1;k=edge[k].next) { to=edge[k].to; if(to==p) continue; DP(to,u); dp[u][0]+=min(dp[to][0],dp[to][1]); dp[u][1]+=dp[to][0]; } } 对于最大独立集,动态规划函数如下: voidDP() { dp[u][0]=1; dp[u][1]=0; intk,to; for(k=head[u];k! =-1;k=edge[k].next) { to=edge[k].to; if(to==p) continue; DP(to,u); dp[u][0]+=dp[to][1]; dp[u][1]+=max(dp[to][0],dp[to][1]); } } 二分图最大匹配 模板代码 #include #include usingnamespacestd; intn,k;//n矩阵规格,k星体数量 intV1,V2;//二分图顶点集 /*矩阵的行列分别属于二分图的两个顶点集V1、V2,其中行x∈V1,列y∈V2*/ boolgrid[501][501];//存储数据方式: 可达矩阵 boolvis[501];//记录V2的点每个点是否已被搜索过 intlink[501];//记录V2中的点y在V1中所匹配的点x的编号 intm;//最大匹配数 booldfs(intx) { for(inty=1;y<=V2;y++) if(grid[x][y]&&! vis[y])//x到y相邻(有边)且节点y未被搜索 { vis[y]=true;//标记节点y已被搜索 if(link[y]==0||dfs(link[y]))//link[y]==0: 如果y不属于前一个匹配M {//find(link[y]: 如果被y匹配到的节点可以寻找到增广路 link[y]=x;//那么可以更新匹配M'(用M替代M') returntrue;//返回匹配成功的标志 } } returnfalse;//继续查找V1下一个x的邻接节点 } voidsearch(void) { for(intx=1;x<=V1;x++) { memset(vis,false,sizeof(vis));//清空上次搜索时的标记 if(dfs(x))//从V1中的节点x开始寻找增广路 m++; } return; } intmain(void) { cin>>n>>k; V1=V2=n; intx,y;//坐标(临时变量) for(inti=1;i<=k;i++) { cin>>x>>y; grid[x][y]=true;//相邻节点标记 } search(); cout< return0; } 强连通 voidtarjan(inti) { intj; DFN[i]=LOW[i]=++Dindex; instack[i]=true; Stap[++Stop]=i; for(edge*e=V[i];e;e=e->next) { j=e->t; if(! DFN[j]) { tarjan(j); if(LOW[j] LOW[i]=LOW[j]; } elseif(instack[j]&&DFN[j] LOW[i]=DFN[j]; } if(DFN[i]==LOW[i]) { Bcnt++; do { j=Stap[Stop--]; instack[j]=false; Belong[j]=Bcnt; } while(j! =i); } } voidsolve() { inti; Stop=Bcnt=Dindex=0; memset(DFN,0,sizeof(DFN)); for(i=1;i<=N;i++) if(! DFN[i]) tarjan(i); }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法
![提示](https://static.bdocx.com/images/bang_tan.gif)