之前写过 关于网络流的算法入门,其实那么多会一个就OK.首选Dinic,递归很好写25行.
邻接表时候若是无向图则是四条边
解决和值问题,都并入汇点
找多条不同的路径,最小费用流问题.
J 模板题
测试Dinic模板
K 电脑公司,
这个其实也可以不用拆点,不过拆点更容易想,这个是第一个需要构图的题目,完全不会.其实就是不同类型机器之间连接起来输入输出看好就行
N~N+x 为拆后点,之间的cap为工作量
#include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f #define MAXN 200 int G[MAXN][MAXN]; int G2[MAXN][MAXN]; int pre[MAXN][3]; int level[MAXN]; queue<int> Q; int sp, tp; int N,M; struct P{ int w,in[11],out[11]; }pnode[MAXN]; void _DEBUG(){ for(int i=0;i<=N;i++){ for(int j=1;j<=N;j++) printf("%d ",G[i][j]); printf("\n"); } } int BFS(){ memset(level,-1,sizeof(level)); Q.push(sp);level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int v=1;v<=N;v++) if(G[u][v] && level[v]==-1){ level[v]=level[u]+1; Q.push(v); } } return level[tp]!=-1; } int extendPath(int u,int beforemin){ int res=0,t; if(u==tp || !beforemin) return beforemin; for(int v=1;v<=N;v++)///u,v联通 v是下一层 获得扩展路径 四个括号 if(G[u][v] && level[v]==level[u]+1 && (t=extendPath(v,min(beforemin,G[u][v])))){ G[u][v]-=t; G[v][u]+=t; res+=t; beforemin-=t; if(!beforemin) break; } if(!res) level[u]=-1;///一个常数优化,该点不能再增加流了 return res; } /** 将一个机器拆点 另一个点是i+N 对于输出的每台机器的连接情况 将原图保存到G2,比对就好了.所有的枚举都是i+N->j 出边到入边 **/ void createG(){ for(int i=1;i<=N;i++){ int flag=1; G[i][i+N]=pnode[i].w; for(int j=0;j<M && flag;j++) if(pnode[i].in[j]==1) flag=0;///构图要注意 全为0或2都行 if(flag) G[sp][i]=inf; flag=1; for(int j=0;j<M && flag;j++) if(pnode[i].out[j]==0) flag=0; if(flag) G[i+N][tp]=inf; for(int j=1;j<=N;j++) if(i!=j) { flag=1; for(int k=0;k<M;k++) if(pnode[i].out[k] + pnode[j].in[k]==1) flag=0; if(flag) G[i+N][j]=inf; } } } int main( ){ while(~scanf( "%d%d",&M,&N )){ for(int i=1;i<=N;i++){ scanf("%d",&pnode[i].w); for(int j=0;j<M;j++) scanf("%d",&pnode[i].in[j]); for(int j=0;j<M;j++) scanf("%d",&pnode[i].out[j]); } memset(G,0,sizeof(G)); sp=0;tp=2*N+1;int ans=0,tans; createG(); memcpy(G2,G,sizeof(G)); N=tp;///_DEBUG(); while(BFS()) while(tans=extendPath(sp,inf)) ans+=tans; int num=0;N/=2; for(int i=N+1;i<=2*N;i++) for(int j=1;j<=N;j++) if(i!=j+N && G2[i][j]!=G[i][j]){ pre[num][0]=i-N; pre[num][1]=j; pre[num][2]=G2[i][j]-G[i][j]; num++; } printf("%d %d\n",ans,num); for(int i=0;i<num;i++) printf("%d %d %d\n",pre[i][0],pre[i][1],pre[i][2]); } return 0; }
L 求奶牛和挤奶器匹配前提下,奶牛走的路程最短.
这道题难点在于读懂,看明白了就好搞,其限制是奶牛和挤奶器连边的条件 .二分法很快
M 神题啊,客户去猪,然后将猪圈里的猪重新分配下,使得可以获得的猪数量最多.换个角度,猪圈的猪实际上就是第一个可以接触的人重新分配,后面的人通过他
拿猪.规约不好想.点击打开链接
M 通过矩阵的每行和,每列和,每个元的限制条件得出矩阵.
这里矩阵的每个元素是通过点之间的流量表明的,即边.刚开始将所有的点都纳入,忒多了.
源点连每行和,每列和连汇点.
F 裸 不过可以用贪心去做,策略:使得后面上界大的有更多选择.
/** 题解: 贪心即可。 先将翻晒霜按spf值排序,从小到大处理。 对于当前处理的防晒霜,找到能使用它且spf上界最小的牛,将防晒霜供其使用。 因为上界越大,选择越多,在之后可能也可以找到匹配的防晒霜, 而由于从小到大处理,下界已经没有意义(不可能找到比当前spf更小的匹配),这是贪心的原则。 复杂度:O(l*logl+n*l) 转载自http://blog.163.com/benz_/blog/static/18684203020115612011262/ 使得后面有更多的选择 eg 教室排时间 **/ #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=5010; int N,M,T; int level[MAXN]; int vis[MAXN]; struct P{ int u,v,cap,cost,nxt; }node[2000005];int tot,head[MAXN]; void _DEBUG(int N,int (*G)[MAXN]){ printf("\n"); for(int i=0;i<=N;i++){ printf("\n"); for(int j=0;j<=N;j++) printf("%5d ",G[i][j]); }printf("\n"); } bool bfs(int sp,int tp,int NN){ memset(level,-1,sizeof(level)); queue<int> Q; Q.push(sp); level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){ Q.push(node[i].v); level[node[i].v]=level[u]+1; } } return level[tp]!=-1; } int extendPath(int sp,int tp,int NN,int u,int beforemin){ int res=0,t; if(u==tp || beforemin==0) return beforemin; for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){ node[i].cap-=t; node[i^1].cap+=t; beforemin-=t; res+=t; if(!beforemin) break; } if(!res) level[u]=-1; return res; } int pre[MAXN]; int dist[MAXN]; int spfa(int sp,int tp,int NN){ queue<int> Q; memset(vis,0,sizeof(vis)); memset(dist,0x3f,sizeof(dist)); dist[sp]=0;vis[sp]=1;pre[sp]=-1; Q.push(sp); while(Q.size()){ int u=Q.front();Q.pop();vis[u]=0; for(int i=head[u];~i;i=node[i].nxt) { int v=node[i].v; if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){ dist[v]=dist[u]+node[i].cost; pre[v]=i; if(!vis[v]) vis[v]=1,Q.push(v); } } } return dist[tp]!=inf; } int mcmf(int sp,int tp,int NN){ int ans=0,flow_sum=0; while(spfa(sp,tp,NN)){ int t=inf; for(int i=pre[tp];i!=-1;i=pre[node[i].u]) t=min(t,node[i].cap); for(int i=pre[tp];i!=-1;i=pre[node[i].u]){ node[i].cap-=t; node[i^1].cap+=t; }ans+= dist[tp]*t;flow_sum+=t; } return ans; } void add(int a,int b,int c,int d){ node[tot].u=a;node[tot].v=b; node[tot].cap=c;node[tot].cost=d; node[tot].nxt=head[a];head[a]=tot++; node[tot].u=b;node[tot].v=a; node[tot].cap=0;node[tot].cost=-d; node[tot].nxt=head[b];head[b]=tot++; } struct COW{ int spf1,spf2,id; }SPF[MAXN]; bool cmp(const struct COW &a,const struct COW &b){ return a.spf1<b.spf1; } int main(){ while(~scanf("%d%d",&N,&M)){ tot=0; memset(head,-1,sizeof(head)); int sp=0,tp=N+M+1; for(int i=1;i<=N;i++) add(sp,i,1,0),scanf("%d%d",&SPF[i].spf1,&SPF[i].spf2),SPF[i].id=i; ///sort(SPF+1,SPF+1+N,cmp); for(int i=1;i<=M;i++){ int t,k; scanf("%d%d",&t,&k); add(i+N,tp,k,0); for(int j=1;j<=N;j++){ if(t>=SPF[j].spf1 && t<=SPF[j].spf2){ /* if(i==608 && j==105) k-=2;*/ add(SPF[j].id,i+N,1,0); /// SPF[i].id 这里写错了浪费时间 node 太少了无限RE } } } int ans=0,tans; while(bfs(sp,tp,tp+1)) while(tans=extendPath(sp,tp,tp+1,sp,inf)) ans+=tans; printf("%d\n",ans); } return 0; }
A 将350天都当做结点,起初我只是将7天当做了结点.源点连接电影为D,电影连接7天为week,7天连汇点为可连接到汇点电影中的最大值.
这样就忽视了时间顺序这个条件.
/** 起初以为79个点就够了,源点到电影 cap=day,电影到7天 cap=week,7天到汇点为最大week 没有考虑时间先后关系,即条件变宽了,eg两个电影都要求2week完成但是第三个电影宽限 为4week,借着这条路就也能完成要求 **/ #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=410; int N,M,T; int sp,tp; int level[MAXN]; int vis[MAXN]; struct P{ int u,v,cap,cost,nxt; }node[MAXN*MAXN];int tot,head[MAXN]; void _DEBUG(int N,int (*G)[MAXN]){ printf("\n"); for(int i=0;i<=N;i++){ printf("\n"); for(int j=0;j<=N;j++) printf("%5d ",G[i][j]); }printf("\n"); } bool bfs(int sp,int tp,int NN){ memset(level,-1,sizeof(level)); queue<int> Q; Q.push(sp); level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){ Q.push(node[i].v); level[node[i].v]=level[u]+1; } } return level[tp]!=-1; } int extendPath(int sp,int tp,int NN,int u,int beforemin){ int res=0,t; if(u==tp || beforemin==0) return beforemin; for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){ node[i].cap-=t; node[i^1].cap+=t; beforemin-=t; res+=t; if(!beforemin) break; } if(!res) level[u]=-1; return res; } int pre[MAXN]; int dist[MAXN]; int spfa(int sp,int tp,int NN){ queue<int> Q; memset(vis,0,sizeof(vis)); memset(dist,0x3f,sizeof(dist)); dist[sp]=0;vis[sp]=1;pre[sp]=-1; Q.push(sp); while(Q.size()){ int u=Q.front();Q.pop();vis[u]=0; for(int i=head[u];~i;i=node[i].nxt) { int v=node[i].v; if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){ dist[v]=dist[u]+node[i].cost; pre[v]=i; if(!vis[v]) vis[v]=1,Q.push(v); } } } return dist[tp]!=inf; } int mcmf(int sp,int tp,int NN){ int ans=0,flow_sum=0; while(spfa(sp,tp,NN)){ int t=inf; for(int i=pre[tp];i!=-1;i=pre[node[i].u]) t=min(t,node[i].cap); for(int i=pre[tp];i!=-1;i=pre[node[i].u]){ node[i].cap-=t; node[i^1].cap+=t; }ans+= dist[tp]*t;flow_sum+=t; } return ans; } void add(int a,int b,int c,int d){ node[tot].u=a;node[tot].v=b; node[tot].cap=c;node[tot].cost=d; node[tot].nxt=head[a];head[a]=tot++; node[tot].u=b;node[tot].v=a; node[tot].cap=0;node[tot].cost=-d; node[tot].nxt=head[b];head[b]=tot++; } int in[MAXN][10]; int main(){ for(scanf("%d",&T);T;T--){ scanf("%d",&N); tot=0;sp=0;tp=371; memset(head,-1,sizeof(head)); for(int i=1;i<=350;i++) add(i,371,1,0); int tmp=0; for(int i=1;i<=N;i++) for(int j=1;j<=9;j++) scanf("%d",in[i]+j); for(int i=1;i<=N;i++){ add(sp,350+i,in[i][8],0); tmp+=in[i][8]; for(int j=1;j<=7;j++) if(in[i][j]==1) for(int k=0;k<in[i][9];k++) add(350+i,k*7+j,1,0); } int ans=0,tans; while(bfs(sp,tp,372)) while(tans=extendPath(sp,tp,372,sp,inf)) ans+=tans; printf("%s\n",ans==tmp?"Yes":"No"); ///printf("%d\n",-mcmf(sp,num,num)); } return 0; }
B 残余网络找割边,两次dfs找到S,T可到达点.枚举边即可(边cap=0,两点分别在S,T可达)
/** 邻接表转矩阵,正图,反置图 **/ #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=510; int N,M,T; int sp,tp; int G[MAXN][MAXN]; int level[MAXN]; int vis[MAXN]; struct P{ int u,v,cap,cost,nxt; }node[MAXN*MAXN];int tot,head[MAXN]; void _DEBUG(int N,int (*G)[MAXN]){ printf("\n"); for(int i=0;i<=N;i++){ printf("\n"); for(int j=0;j<=N;j++) printf("%5d ",G[i][j]); }printf("\n"); } bool bfs(int sp,int tp,int NN){ memset(level,-1,sizeof(level)); queue<int> Q; Q.push(sp); level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){ Q.push(node[i].v); level[node[i].v]=level[u]+1; } } return level[tp]!=-1; } int extendPath(int sp,int tp,int NN,int u,int beforemin){ int res=0,t; if(u==tp || beforemin==0) return beforemin; for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){ node[i].cap-=t; node[i^1].cap+=t; beforemin-=t; res+=t; if(!beforemin) break; } if(!res) level[u]=-1; return res; } int pre[MAXN]; int dist[MAXN]; int spfa(int sp,int tp,int NN){ queue<int> Q; memset(vis,0,sizeof(vis)); memset(dist,0x3f,sizeof(dist)); dist[sp]=0;vis[sp]=1;pre[sp]=-1; Q.push(sp); while(Q.size()){ int u=Q.front();Q.pop();vis[u]=0; for(int i=head[u];~i;i=node[i].nxt) { int v=node[i].v; if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){ dist[v]=dist[u]+node[i].cost; pre[v]=i; if(!vis[v]) vis[v]=1,Q.push(v); } } } return dist[tp]!=inf; } int mcmf(int sp,int tp,int NN){ int ans=0,flow_sum=0; while(spfa(sp,tp,NN)){ int t=inf; for(int i=pre[tp];i!=-1;i=pre[node[i].u]) t=min(t,node[i].cap); for(int i=pre[tp];i!=-1;i=pre[node[i].u]){ node[i].cap-=t; node[i^1].cap+=t; }ans+= dist[tp]*t;flow_sum+=t; } return ans; } void add(int a,int b,int c,int d){ node[tot].u=a;node[tot].v=b; node[tot].cap=c;node[tot].cost=d; node[tot].nxt=head[a];head[a]=tot++; node[tot].u=b;node[tot].v=a; node[tot].cap=0;node[tot].cost=-d; node[tot].nxt=head[b];head[b]=tot++; } void dfs(int u,int vis[MAXN],int N){ vis[u]=1; for(int i=1;i<N;i++) if(!vis[i] && G[u][i]) dfs(i,vis,N); } int vis1[MAXN],vis2[MAXN]; int main(){ while(~scanf("%d%d",&N,&M)){ tot=0;sp=0;tp=N-1; memset(head,-1,sizeof(head)); for(int i=0;i<M;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c,0); } int tans; while(bfs(sp,tp,N)) while(tans=extendPath(sp,tp,N,sp,inf)); memset(G,0,sizeof(G)); memset(vis1,0,sizeof(vis)); for(int i=0;i<tot;i+=2) if(node[i].cap) G[node[i].u][node[i].v]=1; dfs(sp,vis1,N); memset(G,0,sizeof(G)); memset(vis2,0,sizeof(vis)); for(int i=0;i<tot;i+=2) if(node[i].cap) G[node[i].v][node[i].u]=1; dfs(tp,vis2,N); int ans=0; for(int i=0;i<tot;i+=2) if(node[i].cap==0 && vis1[node[i].u] && vis2[node[i].v]) ans++; printf("%d\n",ans); ///printf("%d\n",-mcmf(sp,num,num)); } return 0; }
C 边是否可连受到限制.范围很小只有20,二分的话注意h的处理且不能l<=h
int main(){ while(~scanf("%d%d",&N,&M)){ int tans,ans; tot=0;sp=0;tp=N+M+1; memset(head,-1,sizeof(head)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("%d",in[i]+j); for(int i=1;i<=M;i++) scanf("%d",in[N+1]+i); int l=0,h=M; while(l<h){ int len=(l+h)>>1; int flag=1; for(int s=1;s+len<=M && flag;s++){ tot=0;memset(head,-1,sizeof(head)); for(int k=1;k<=N;k++) add(sp,k,1,0); for(int k=1;k<=M;k++) add(k+N,tp,in[N+1][k],0); for(int k=1;k<=N;k++) for(int t=s;t<=s+len;t++) add(k,N+in[k][t],1,0); ans=0; while(bfs(sp,tp,tp+1)) while(tans=extendPath(sp,tp,tp+1,sp,inf)) ans+=tans; if(ans==N) flag=0; } if(flag) l=len+1; else h=len; } printf("%d\n",l+1);
D 欧拉混合图
/* 1 定义 欧拉通路 (Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。 欧拉回路 (Euler circuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。 欧拉图——存在欧拉回路的图。 2 无向图是否具有欧拉通路或回路的判定 G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。 G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。 3 有向图是否具有欧拉通路或回路的判定 D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。 D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。 4 混合图。混合图也就是无向图与有向图的混合,即图中的边既有有向边也有无向边。 5 混合图欧拉回路 混合图欧拉回路用的是网络流。 把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路。 现在每个点入度和出度之差均为偶数。将这个偶数除以2,得x。即是说,对于每一个点,只要将x条边反向(入>出就是变入,出>入就是变出),就能保证出 = 入。如果每个点都是出 = 入,那么很明显,该图就存在欧拉回路。 现 在的问题就变成了:该改变哪些边,可以让每个点出 = 入?构造网络流模型。有向边不能改变方向,直接删掉。开始已定向的无向边,定的是什么向,就把网络构建成什么样,边长容量上限1。另新建s和t。对于入 > 出的点u,连接边(u, t)、容量为x,对于出 > 入的点v,连接边(s, v),容量为x(注意对不同的点x不同。当初由于不小心,在这里错了好几次)。之后,察看是否有满流的分配。有就是能有欧拉回路,没有就是没有。查看流值 分配,将所有流量非 0(上限是1,流值不是0就是1)的边反向,就能得到每点入度 = 出度的欧拉图。 由于是满流,所以每个入 > 出的点,都有x条边进来,将这些进来的边反向,OK,入 = 出了。对于出 > 入的点亦然。那么,没和s、t连接的点怎么办?和s连接的条件是出 > 入,和t连接的条件是入 > 出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入 = 出了。那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。 所以,就这样,混合图欧拉回路问题,解了。 *以上内容转自http://www.cnblogs.com/destinydesigner/archive/2009/09/28/1575674.html/ /* 混合图的欧拉回路,所谓混合图就是一个图中既有有向边,又有无向边 */ #include <iostream> #include <cmath> #include <queue> #define MAX_N 200 using namespace std; int deg[MAX_N + 1]; int gf[MAX_N + 2][MAX_N + 2]; int f[MAX_N + 2][MAX_N + 2]; int total, m, s; queue<int> bfsq; bool v[MAX_N + 2]; int pre[MAX_N + 2]; int bfs() { int i; for(i = 0; i <= m + 1; i++) { v[i] = false; pre[i] = -1; } v[0] = true; while(!bfsq.empty()) bfsq.pop(); bfsq.push(0); while(!bfsq.empty()) { int curNode; curNode = bfsq.front(); bfsq.pop(); for(i = 0; i <= m + 1; i++) { if(i == curNode || v[i] || !gf[curNode][i]) continue; v[i] = true; pre[i] = curNode; bfsq.push(i); } } if(pre[m + 1] == -1) return -1; int minVal = INT_MAX, curNode = m + 1, preNode; while((preNode = pre[curNode]) != -1) { if(gf[preNode][curNode] < minVal) minVal = gf[preNode][curNode]; curNode = preNode; } return minVal; } bool admondsKarp(int total) { int cutVal; while((cutVal = bfs()) != -1) { int curNode = m + 1, preNode; while((preNode = pre[curNode]) != -1) { gf[preNode][curNode] -= cutVal; gf[curNode][preNode] += cutVal; f[preNode][curNode] += cutVal; f[curNode][preNode] = - f[preNode][curNode]; curNode = preNode; } } int totalVal = 0; for(int i = 1; i <= m; i++) totalVal += f[0][i]; return totalVal == total; } int main() { int caseN, i, from, to, type; scanf("%d", &caseN); while(caseN--) { scanf("%d%d", &m, &s); memset(deg, 0, sizeof(deg)); memset(gf, 0, sizeof(gf)); memset(f, 0, sizeof(f)); for(i = 0; i < s; i++) { scanf("%d%d%d", &from, &to, &type); deg[from]++; deg[to]--; if(type != 1) gf[from][to]++; } bool can = true; for(i = 1; i <= m; i++) { if(abs(deg[i]) % 2 == 1) { can = false; break; } else deg[i] = deg[i] / 2; } if(can) { total = 0; for(i = 1; i <= m; i++) { if(deg[i] < 0) gf[i][m + 1] = abs(deg[i]); else if(deg[i] > 0) { gf[0][i] = deg[i]; total += deg[i]; } } if(!admondsKarp(total)) can = false; } if(!can) printf("impossible/n"); else printf("possible/n"); } return 0; }
最小费用流问题
用spfa(有负边)找最短路径,增广即可..如果求最大值,将数值取相反值.
E
求使得区间覆盖不超过K次前提下,最大化权值.
每个区间可以选也可以不选, 区间两个数代表的点相连 cap=1,费用为负值,将相邻的边连起来,cap=K,费用0.( 表明可以正常往后流 )
/** 题解: **/ #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=5010; int N,M,T; int level[MAXN]; int vis[MAXN]; struct P{ int u,v,cap,cost,nxt; }node[MAXN];int tot,head[MAXN]; void _DEBUG(int N,int (*G)[MAXN]){ printf("\n"); for(int i=0;i<=N;i++){ printf("\n"); for(int j=0;j<=N;j++) printf("%5d ",G[i][j]); }printf("\n"); } bool bfs(int sp,int tp,int NN){ memset(level,-1,sizeof(level)); queue<int> Q; Q.push(sp); level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){ Q.push(node[i].v); level[node[i].v]=level[u]+1; } } return level[tp]!=-1; } int extendPath(int sp,int tp,int NN,int u,int beforemin){ int res=0,t; if(u==tp || beforemin==0) return beforemin; for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){ node[i].cap-=t; node[i^1].cap+=t; beforemin-=t; res+=t; if(!beforemin) break; } if(!res) level[u]=-1; return res; } int pre[MAXN]; int dist[MAXN]; int spfa(int sp,int tp,int NN){ queue<int> Q; memset(vis,0,sizeof(vis)); memset(dist,0x3f,sizeof(dist)); dist[sp]=0;vis[sp]=1;pre[sp]=-1; Q.push(sp); while(Q.size()){ int u=Q.front();Q.pop();vis[u]=0; for(int i=head[u];~i;i=node[i].nxt) { int v=node[i].v; if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){ dist[v]=dist[u]+node[i].cost; pre[v]=i; if(!vis[v]) vis[v]=1,Q.push(v); } } } return dist[tp]!=inf; } int mcmf(int sp,int tp,int NN){ int ans=0,flow_sum=0; while(spfa(sp,tp,NN)){ int t=inf; for(int i=pre[tp];i!=-1;i=pre[node[i].u]) t=min(t,node[i].cap); for(int i=pre[tp];i!=-1;i=pre[node[i].u]){ node[i].cap-=t; node[i^1].cap+=t; }ans+= dist[tp]*t;flow_sum+=t; } return ans; } void add(int a,int b,int c,int d){ node[tot].u=a;node[tot].v=b; node[tot].cap=c;node[tot].cost=d; node[tot].nxt=head[a];head[a]=tot++; node[tot].u=b;node[tot].v=a; node[tot].cap=0;node[tot].cost=-d; node[tot].nxt=head[b];head[b]=tot++; } int data[MAXN][3]; int ar[MAXN<<2]; int lisan[MAXN*MAXN]; int main(){ for(scanf("%d",&T);T;T--){ scanf("%d%d",&N,&M); tot=0; memset(head,-1,sizeof(head)); int sp=0,tp=N+M+1; int num=1; for(int i=1;i<=N;i++) scanf("%d%d%d",data[i]+0,data[i]+1,data[i]+2),ar[num++]=data[i][0],ar[num++]=data[i][1]; sort(ar+1,ar+num);int j=1; for(int i=2;i<num;i++) if(ar[j]!=ar[i]) ar[++j]=ar[i]; num=j+1; add(0,1,M,0); for(int i=1;i<num;i++) lisan[ar[i]]=i,add(i,i+1,M,0); for(int i=1;i<=N;i++) add(lisan[data[i][0]],lisan[data[i][1]],1,-data[i][2]); printf("%d\n",-mcmf(sp,num,num)); } return 0; }
H
求两条不重合的最短路径,cap=1,dist=费用.表达了边只能用一次.
/** 无向图的网络流只能用邻接表,因为其实是两个方向,每个方向都有自己的增广路,残余等 spfa,直接搞定 **/ #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=10010; int N,M; int cap[MAXN][MAXN],flow[MAXN][MAXN];///cap[][]网络中的容量,flow[][]流量分配 int cost[MAXN][MAXN]; int level[MAXN]; int vis[MAXN]; struct P{ int u,v,cap,cost,nxt; }node[MAXN<<3];int tot,head[MAXN]; void _DEBUG(int N,int (*G)[MAXN]){ printf("\n"); for(int i=0;i<=N;i++){ printf("\n"); for(int j=0;j<=N;j++) printf("%5d ",G[i][j]); }printf("\n"); } bool bfs(int sp,int tp,int NN){ memset(level,-1,sizeof(level)); queue<int> Q; Q.push(sp); level[sp]=0; while(Q.size()){ int u=Q.front();Q.pop(); for(int v=1;v<=NN;v++) if(cap[u][v] && level[v]==-1){ Q.push(v); level[v]=level[u]+1; } } return level[tp]!=-1; } int extendPath(int sp,int tp,int NN,int u,int beforemin){ int res=0,t; if(u==tp || beforemin==0) return beforemin; for(int v=1;v<=NN;v++) if(cap[u][v] && level[v]==level[u]+1 && (t=extendPath(sp,tp,NN,v,min(beforemin,cap[u][v])))){ cap[u][v]-=t; cap[v][u]+=t; beforemin-=t; res+=t; if(!beforemin) break; } if(!res) level[u]=-1; return res; } int pre[MAXN]; int dist[MAXN]; int spfa(int sp,int tp,int NN){ queue<int> Q; memset(vis,0,sizeof(vis)); memset(dist,0x3f,sizeof(dist)); dist[sp]=0;vis[sp]=1;pre[sp]=-1; Q.push(sp); while(Q.size()){ int u=Q.front();Q.pop();vis[u]=0; for(int i=head[u];~i;i=node[i].nxt) { int v=node[i].v; if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){ dist[v]=dist[u]+node[i].cost; pre[v]=i; if(!vis[v]) vis[v]=1,Q.push(v); } } } return dist[tp]!=inf; } int mcmf(int sp,int tp,int NN){ int ans=0,flow_sum=0; while(spfa(sp,tp,NN)){ int t=inf; for(int i=pre[tp];i!=-1;i=pre[node[i].u]) t=min(t,node[i].cap); for(int i=pre[tp];i!=-1;i=pre[node[i].u]){ node[i].cap-=t; node[i^1].cap+=t; }ans+= dist[tp]*t;flow_sum+=t; } return ans; } void add(int a,int b,int c,int d){ node[tot].u=a;node[tot].v=b; node[tot].cap=c;node[tot].cost=d; node[tot].nxt=head[a];head[a]=tot++; node[tot].u=b;node[tot].v=a; node[tot].cap=0;node[tot].cost=-d; node[tot].nxt=head[b];head[b]=tot++; } int main(){ while(~scanf("%d%d",&N,&M)){ tot=0; memset(head,-1,sizeof(head)); add(0,1,2,0); add(N,N+1,2,0); for(int i=1;i<=M;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,1,c); add(b,a,1,c); } printf("%d\n",mcmf(0,N+1,N+1)); } return 0; }